diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 3baab4bd4ef..7e1849feba4 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -474,6 +474,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt index 3d4a0fdecd7..41c3feab6c8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AbstractReferenceSubstitutionRenameHandler.kt @@ -23,9 +23,11 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import com.intellij.psi.PsiWhiteSpace import com.intellij.refactoring.rename.PsiElementRenameHandler import com.intellij.refactoring.rename.inplace.MemberInplaceRenameHandler import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtSimpleNameExpression @@ -37,7 +39,11 @@ abstract class AbstractReferenceSubstitutionRenameHandler() ?: return null + var elementAtCaret = ktFile.findElementAt(caret.offset) + if (elementAtCaret is PsiWhiteSpace) { + elementAtCaret = CodeInsightUtils.getElementAtOffsetIgnoreWhitespaceAfter(ktFile, caret.offset) + } + return elementAtCaret?.getNonStrictParentOfType() } protected abstract fun getTargetDescriptor(dataContext: DataContext): D? diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameFunctionByLabeledReferenceInLambdaArgumentHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameFunctionByLabeledReferenceInLambdaArgumentHandler.kt new file mode 100644 index 00000000000..9f120f4b9ca --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameFunctionByLabeledReferenceInLambdaArgumentHandler.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2017 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.kotlin.idea.refactoring.rename + +import com.intellij.openapi.actionSystem.DataContext +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.references.getCalleeByLambdaArgument +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtLabelReferenceExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class RenameFunctionByLabeledReferenceInLambdaArgumentHandler : AbstractReferenceSubstitutionRenameHandler() { + override fun getTargetDescriptor(dataContext: DataContext): FunctionDescriptor? { + val refExpr = getReferenceExpression(dataContext) as? KtLabelReferenceExpression ?: return null + val context = refExpr.analyze(BodyResolveMode.PARTIAL) + val lambda = context[BindingContext.LABEL_TARGET, refExpr] as? KtFunction ?: return null + val calleeExpression = lambda.getCalleeByLambdaArgument() ?: return null + return context[BindingContext.REFERENCE_TARGET, calleeExpression] as? FunctionDescriptor + } +} diff --git a/idea/testData/refactoring/rename/funWithLabeledReturns/after/test.kt b/idea/testData/refactoring/rename/funWithLabeledReturns/after/test.kt new file mode 100644 index 00000000000..1ef00f64f04 --- /dev/null +++ b/idea/testData/refactoring/rename/funWithLabeledReturns/after/test.kt @@ -0,0 +1,7 @@ +fun bar(f: () -> R) = f() + +fun test() { + bar { return@bar false } + + bar(fun(): Boolean { return@bar false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/funWithLabeledReturns/before/test.kt b/idea/testData/refactoring/rename/funWithLabeledReturns/before/test.kt new file mode 100644 index 00000000000..399bc3bb5b2 --- /dev/null +++ b/idea/testData/refactoring/rename/funWithLabeledReturns/before/test.kt @@ -0,0 +1,7 @@ +fun /*rename*/foo(f: () -> R) = f() + +fun test() { + foo { return@foo false } + + foo(fun(): Boolean { return@foo false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/funWithLabeledReturns/funWithLabeledReturns.test b/idea/testData/refactoring/rename/funWithLabeledReturns/funWithLabeledReturns.test new file mode 100644 index 00000000000..9e08b548cbd --- /dev/null +++ b/idea/testData/refactoring/rename/funWithLabeledReturns/funWithLabeledReturns.test @@ -0,0 +1,5 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test.kt", + "newName": "bar" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/after/test.kt b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/after/test.kt new file mode 100644 index 00000000000..345dbd8a9ed --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/after/test.kt @@ -0,0 +1,5 @@ +fun bar(f: () -> R) = f() + +fun test() { + bar(fun(): Boolean { return@bar false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/before/test.kt b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/before/test.kt new file mode 100644 index 00000000000..a7ae0517cb2 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/before/test.kt @@ -0,0 +1,5 @@ +fun foo(f: () -> R) = f() + +fun test() { + foo(fun(): Boolean { return@/*rename*/foo false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/labeledReturnInAnonymousFun.test b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/labeledReturnInAnonymousFun.test new file mode 100644 index 00000000000..29ce6c16b62 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFun/labeledReturnInAnonymousFun.test @@ -0,0 +1,5 @@ +{ + "type": "AUTO_DETECT", + "mainFile": "test.kt", + "newName": "bar" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/after/test.kt b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/after/test.kt new file mode 100644 index 00000000000..345dbd8a9ed --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/after/test.kt @@ -0,0 +1,5 @@ +fun bar(f: () -> R) = f() + +fun test() { + bar(fun(): Boolean { return@bar false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/before/test.kt b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/before/test.kt new file mode 100644 index 00000000000..c4c32af4655 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/before/test.kt @@ -0,0 +1,5 @@ +fun foo(f: () -> R) = f() + +fun test() { + foo(fun(): Boolean { return@foo/*rename*/ false }) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/labeledReturnInAnonymousFunBeforeWhitespace.test b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/labeledReturnInAnonymousFunBeforeWhitespace.test new file mode 100644 index 00000000000..29ce6c16b62 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/labeledReturnInAnonymousFunBeforeWhitespace.test @@ -0,0 +1,5 @@ +{ + "type": "AUTO_DETECT", + "mainFile": "test.kt", + "newName": "bar" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInLambda/after/test.kt b/idea/testData/refactoring/rename/labeledReturnInLambda/after/test.kt new file mode 100644 index 00000000000..c6d8d3dc977 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInLambda/after/test.kt @@ -0,0 +1,7 @@ +fun bar(f: () -> R) = f() + +fun test() { + bar { + return@bar false + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInLambda/before/test.kt b/idea/testData/refactoring/rename/labeledReturnInLambda/before/test.kt new file mode 100644 index 00000000000..3095314ebc1 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInLambda/before/test.kt @@ -0,0 +1,7 @@ +fun foo(f: () -> R) = f() + +fun test() { + foo { + return@/*rename*/foo false + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/labeledReturnInLambda/labeledReturnInLambda.test b/idea/testData/refactoring/rename/labeledReturnInLambda/labeledReturnInLambda.test new file mode 100644 index 00000000000..29ce6c16b62 --- /dev/null +++ b/idea/testData/refactoring/rename/labeledReturnInLambda/labeledReturnInLambda.test @@ -0,0 +1,5 @@ +{ + "type": "AUTO_DETECT", + "mainFile": "test.kt", + "newName": "bar" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index 5b0c4952b76..f6d24e88c6f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -204,6 +204,30 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("funWithLabeledReturns/funWithLabeledReturns.test") + public void testFunWithLabeledReturns_FunWithLabeledReturns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/funWithLabeledReturns/funWithLabeledReturns.test"); + doTest(fileName); + } + + @TestMetadata("labeledReturnInAnonymousFun/labeledReturnInAnonymousFun.test") + public void testLabeledReturnInAnonymousFun_LabeledReturnInAnonymousFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/labeledReturnInAnonymousFun/labeledReturnInAnonymousFun.test"); + doTest(fileName); + } + + @TestMetadata("labeledReturnInAnonymousFunBeforeWhitespace/labeledReturnInAnonymousFunBeforeWhitespace.test") + public void testLabeledReturnInAnonymousFunBeforeWhitespace_LabeledReturnInAnonymousFunBeforeWhitespace() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/labeledReturnInAnonymousFunBeforeWhitespace/labeledReturnInAnonymousFunBeforeWhitespace.test"); + doTest(fileName); + } + + @TestMetadata("labeledReturnInLambda/labeledReturnInLambda.test") + public void testLabeledReturnInLambda_LabeledReturnInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/labeledReturnInLambda/labeledReturnInLambda.test"); + doTest(fileName); + } + @TestMetadata("lambdaParameterRedeclaration/lambdaParameterRedeclaration.test") public void testLambdaParameterRedeclaration_LambdaParameterRedeclaration() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/lambdaParameterRedeclaration/lambdaParameterRedeclaration.test");