diff --git a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JetLightClass.java b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JetLightClass.java index fe17ef8dde5..51f49df536f 100644 --- a/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JetLightClass.java +++ b/compiler/jet.as.java.psi/src/org/jetbrains/jet/asJava/JetLightClass.java @@ -35,10 +35,7 @@ import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; import com.intellij.psi.impl.light.AbstractLightClass; import com.intellij.psi.stubs.PsiClassHolderFileStub; import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.util.CachedValue; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.*; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.analyzer.AnalyzeExhaust; @@ -49,6 +46,7 @@ import org.jetbrains.jet.codegen.CompilationErrorHandler; import org.jetbrains.jet.codegen.GenerationState; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetFunction; import org.jetbrains.jet.lang.psi.JetPsiUtil; import org.jetbrains.jet.lang.resolve.FqName; import org.jetbrains.jet.lang.resolve.java.*; @@ -243,4 +241,15 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa public static JetLightClass wrapDelegate(JetClass jetClass) { return new JetLightClass(jetClass.getManager(), (JetFile) jetClass.getContainingFile(), JetPsiUtil.getFQName(jetClass)); } + + public static PsiMethod wrapMethod(JetFunction function) { + JetClass containingClass = PsiTreeUtil.getParentOfType(function, JetClass.class); + JetLightClass wrapper = wrapDelegate(containingClass); + for (PsiMethod method : wrapper.getMethods()) { + if (method instanceof PsiCompiledElement && ((PsiCompiledElement) method).getMirror() == function) { + return method; + } + } + return null; + } } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 01f7da8ee16..dfd1ae7a207 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -126,6 +126,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindFunctionUsagesHandler.java b/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindFunctionUsagesHandler.java index 6df300ddee1..3412b5198a6 100644 --- a/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindFunctionUsagesHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/findUsages/KotlinFindFunctionUsagesHandler.java @@ -17,13 +17,10 @@ package org.jetbrains.jet.plugin.findUsages; import com.intellij.find.findUsages.FindUsagesHandler; -import com.intellij.psi.PsiCompiledElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; -import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.asJava.JetLightClass; -import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetClassBody; import org.jetbrains.jet.lang.psi.JetFunction; @@ -40,12 +37,9 @@ public class KotlinFindFunctionUsagesHandler extends FindUsagesHandler { public PsiElement[] getSecondaryElements() { JetFunction function = (JetFunction) getPsiElement(); if (function.getParent() instanceof JetClassBody) { - JetClass containingClass = PsiTreeUtil.getParentOfType(function, JetClass.class); - JetLightClass wrapper = JetLightClass.wrapDelegate(containingClass); - for (PsiMethod method : wrapper.getMethods()) { - if (method instanceof PsiCompiledElement && ((PsiCompiledElement) method).getMirror() == function) { - return new PsiElement[] { method }; - } + final PsiMethod method = JetLightClass.wrapMethod(function); + if (method != null) { + return new PsiElement[] { method }; } } return PsiElement.EMPTY_ARRAY; diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.java new file mode 100644 index 00000000000..9528f4487cd --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.java @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.refactoring.rename; + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiCompiledElement; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.refactoring.rename.RenamePsiElementProcessor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.asJava.JetLightClass; +import org.jetbrains.jet.lang.psi.JetFunction; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author yole + */ +public class RenameKotlinFunctionProcessor extends RenamePsiElementProcessor { + @Override + public boolean canProcessElement(@NotNull PsiElement element) { + if (element instanceof PsiMethod && ((PsiMethod) element).getContainingClass() instanceof JetLightClass) { + return true; + } + return element instanceof JetFunction; + } + + @Override + public PsiElement substituteElementToRename(PsiElement element, @Nullable Editor editor) { + if (element instanceof PsiMethod && element instanceof PsiCompiledElement && + ((PsiMethod) element).getContainingClass() instanceof JetLightClass) { + return ((PsiCompiledElement) element).getMirror(); + } + return super.substituteElementToRename(element, editor); + } + + @NotNull + @Override + public Collection findReferences(PsiElement element) { + if (element instanceof JetFunction) { + List references = new ArrayList(); + references.addAll(ReferencesSearch.search(element).findAll()); + references.addAll(ReferencesSearch.search(JetLightClass.wrapMethod((JetFunction) element)).findAll()); + return references; + } + return super.findReferences(element); + } +} diff --git a/idea/testData/refactoring/rename/renameKotlinMethod/after/RenameKotlinMethod.kt b/idea/testData/refactoring/rename/renameKotlinMethod/after/RenameKotlinMethod.kt new file mode 100644 index 00000000000..95a2c4bbc84 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinMethod/after/RenameKotlinMethod.kt @@ -0,0 +1,5 @@ +package testing.rename + +public open class C { + public fun second() = 1 +} diff --git a/idea/testData/refactoring/rename/renameKotlinMethod/after/testing/JavaClient.java b/idea/testData/refactoring/rename/renameKotlinMethod/after/testing/JavaClient.java new file mode 100644 index 00000000000..acaa2aa2f36 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinMethod/after/testing/JavaClient.java @@ -0,0 +1,9 @@ +package testing; + +import testing.rename.C; + +class JavaClient { + public void foo() { + new C().second(); + } +} diff --git a/idea/testData/refactoring/rename/renameKotlinMethod/before/RenameKotlinMethod.kt b/idea/testData/refactoring/rename/renameKotlinMethod/before/RenameKotlinMethod.kt new file mode 100644 index 00000000000..c274590881d --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinMethod/before/RenameKotlinMethod.kt @@ -0,0 +1,5 @@ +package testing.rename + +public open class C { + public fun first() = 1 +} diff --git a/idea/testData/refactoring/rename/renameKotlinMethod/before/testing/JavaClient.java b/idea/testData/refactoring/rename/renameKotlinMethod/before/testing/JavaClient.java new file mode 100644 index 00000000000..01a19724c03 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinMethod/before/testing/JavaClient.java @@ -0,0 +1,9 @@ +package testing; + +import testing.rename.C; + +class JavaClient { + public void foo() { + new C().first(); + } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameInKotlinTest.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameInKotlinTest.java index 0c103c5da72..1ba76f517d9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameInKotlinTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameInKotlinTest.java @@ -26,16 +26,21 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.MultiFileTestCase; import com.intellij.refactoring.rename.RenameProcessor; +import com.intellij.util.Function; import org.jetbrains.annotations.NonNls; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.FqName; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.TypeProjection; import org.jetbrains.jet.plugin.PluginTestCaseBase; import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade; import java.io.File; +import java.util.Collections; /** * @author Nikolay Krasko @@ -61,7 +66,42 @@ public class RenameInKotlinTest extends MultiFileTestCase { doTestWithRenameClass(new FqName("testing.rename.First"), "Third"); } + public void testRenameKotlinMethod() throws Exception { + doTestWithRenameMethod(new FqName("testing.rename.C"), "first", "second"); + } + + private void doTestWithRenameMethod(final FqName qClassName, final String oldMethodName, String newMethodName) throws Exception { + doTestWithRename(new Function() { + @Override + public PsiElement fun(PsiFile file) { + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) file) + .getBindingContext(); + ClassDescriptor classDescriptor = bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, qClassName); + + assertNotNull(classDescriptor); + final JetScope scope = classDescriptor.getMemberScope(Collections.emptyList()); + final FunctionDescriptor methodDescriptor = scope.getFunctions(oldMethodName).iterator().next(); + return BindingContextUtils.callableDescriptorToDeclaration(bindingContext, methodDescriptor); + } + }, newMethodName); + } + private void doTestWithRenameClass(@NonNls final FqName qClassName, @NonNls final String newName) throws Exception { + doTestWithRename(new Function() { + @Override + public PsiElement fun(PsiFile file) { + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) file) + .getBindingContext(); + ClassDescriptor classDescriptor = bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, qClassName); + + assertNotNull(classDescriptor); + + return BindingContextUtils.classDescriptorToDeclaration(bindingContext, classDescriptor); + } + }, newName); + } + + private void doTestWithRename(@NonNls final Function elementToRenameCallback, @NonNls final String newName) throws Exception { doTest(new PerformAction() { @Override public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception { @@ -80,14 +120,7 @@ public class RenameInKotlinTest extends MultiFileTestCase { return; } - BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile)file) - .getBindingContext(); - ClassDescriptor classDescriptor = bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, qClassName); - - assertNotNull(classDescriptor); - - PsiElement psiElement = BindingContextUtils.classDescriptorToDeclaration(bindingContext, classDescriptor); - + PsiElement psiElement = elementToRenameCallback.fun(file); assertNotNull(psiElement); new RenameProcessor(myProject, psiElement, newName, true, true).run();