From 1bad04db500679f35d72717598e9d9d0e8cfd8ea Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Thu, 2 Mar 2017 23:16:00 +0300 Subject: [PATCH] Fix Can't rename implicit lambda parameter 'it' when caret is placed right after the last character #KT-14401 Fixed --- .../RenameKotlinImplicitLambdaParameter.kt | 8 +- .../idea/refactoring/rename/renameUtil.kt | 9 +- .../inplace/FunctionLiteralItEndCaret.kt | 3 + .../FunctionLiteralItEndCaret.kt.after | 3 + .../idea/refactoring/InplaceRenameTest.kt | 83 ++++++++++--------- 5 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt create mode 100644 idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt index 22275f307bf..99cc3fe795f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -21,17 +21,15 @@ 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.util.PsiTreeUtil import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.psi.KtNameReferenceExpression -class RenameKotlinImplicitLambdaParameter: VariableInplaceRenameHandler() { +class RenameKotlinImplicitLambdaParameter : VariableInplaceRenameHandler() { override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean { - val nameExpression = PsiTreeUtil.findElementOfClassAtOffset( - file, editor.caretModel.offset, KtNameReferenceExpression::class.java, false) + val nameExpression = file.findElementForRename(editor.caretModel.offset) return nameExpression != null && isAutoCreatedItUsage(nameExpression) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt index f128e1d2461..1e8928e5c3b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.idea.refactoring.rename import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil import com.intellij.refactoring.rename.ResolvableCollisionUsageInfo import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo import com.intellij.refactoring.util.MoveRenameUsageInfo @@ -79,4 +81,9 @@ class LostDefaultValuesInOverridingFunctionUsageInfo( subParam.addRange(superParam.equalsToken, defaultValue) } } +} + +inline fun PsiFile.findElementForRename(offset: Int): T? { + return PsiTreeUtil.findElementOfClassAtOffset(this, offset, T::class.java, false) + ?: PsiTreeUtil.findElementOfClassAtOffset(this, (offset - 1).coerceAtLeast(0), T::class.java, false) } \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt b/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt new file mode 100644 index 00000000000..77965fc0447 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt @@ -0,0 +1,3 @@ +fun f() { + val f: (Int) -> Int = { it + it } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt.after b/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt.after new file mode 100644 index 00000000000..9dab32c0735 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/FunctionLiteralItEndCaret.kt.after @@ -0,0 +1,3 @@ +fun f() { + val f: (Int) -> Int = { y -> y + y } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt index 7d9f9e36b6c..f8603d6b5ed 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt @@ -28,6 +28,8 @@ import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaPa import com.intellij.codeInsight.template.impl.TemplateManagerImpl import com.intellij.codeInsight.template.TemplateManager import com.intellij.openapi.command.WriteCommandAction +import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename +import org.jetbrains.kotlin.psi.KtNameReferenceExpression class InplaceRenameTest : LightPlatformCodeInsightTestCase() { override fun isRunInWriteAction(): Boolean = false @@ -50,19 +52,58 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { } fun testFunctionLiteralIt() { + doTestImplicitLambdaParameter("y") + } + + fun testFunctionLiteralItEndCaret() { + doTestImplicitLambdaParameter("y") + } + + fun testFunctionLiteralParenthesis() { + doTestInplaceRename("y") + } + + fun testLocalFunction() { + doTestInplaceRename("bar") + } + + fun testFunctionParameterNotInplace() { + doTestInplaceRename(null) + } + + fun testGlobalFunctionNotInplace() { + doTestInplaceRename(null) + } + + fun testTopLevelValNotInplace() { + doTestInplaceRename(null) + } + + fun testLabelFromFunction() { + doTestInplaceRename("foo") + } + + fun testMultiDeclaration() { + doTestInplaceRename("foo") + } + + fun testLocalVarShadowingMemberProperty() { + doTestInplaceRename("name1") + } + + private fun doTestImplicitLambdaParameter(newName: String) { configureByFile(getTestName(false) + ".kt") - val newName = "y" // This code is copy-pasted from CodeInsightTestUtil.doInlineRename() and slightly modified. // Original method was not suitable because it expects renamed element to be reference to other or referrable - val file = LightPlatformCodeInsightTestCase.getFile()!! - val editor = LightPlatformCodeInsightTestCase.getEditor()!! - val element = file.findReferenceAt(editor.caretModel.offset)!!.element + val file = getFile()!! + val editor = getEditor()!! + val element = file.findElementForRename(editor.caretModel.offset)!! assertNotNull(element) val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, element!!, - LightPlatformCodeInsightTestCase.getCurrentEditorDataContext()) + getCurrentEditorDataContext()) val handler = RenameKotlinImplicitLambdaParameter() assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element) @@ -100,38 +141,6 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { checkResultByFile(getTestName(false) + ".kt.after") } - fun testFunctionLiteralParenthesis() { - doTestInplaceRename("y") - } - - fun testLocalFunction() { - doTestInplaceRename("bar") - } - - fun testFunctionParameterNotInplace() { - doTestInplaceRename(null) - } - - fun testGlobalFunctionNotInplace() { - doTestInplaceRename(null) - } - - fun testTopLevelValNotInplace() { - doTestInplaceRename(null) - } - - fun testLabelFromFunction() { - doTestInplaceRename("foo") - } - - fun testMultiDeclaration() { - doTestInplaceRename("foo") - } - - fun testLocalVarShadowingMemberProperty() { - doTestInplaceRename("name1") - } - private fun doTestInplaceRename(newName: String?) { configureByFile(getTestName(false) + ".kt") val element = TargetElementUtilBase.findTargetElement(