Fix Can't rename implicit lambda parameter 'it' when caret is placed right after the last character

#KT-14401 Fixed
This commit is contained in:
Kirill Rakhman
2017-03-02 23:16:00 +03:00
committed by Alexey Sedunov
parent e3391175d9
commit 1bad04db50
5 changed files with 63 additions and 43 deletions
@@ -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<KtNameReferenceExpression>(editor.caretModel.offset)
return nameExpression != null && isAutoCreatedItUsage(nameExpression)
}
@@ -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 <reified T : PsiElement> 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)
}
@@ -0,0 +1,3 @@
fun f() {
val f: (Int) -> Int = { it<caret> + it }
}
@@ -0,0 +1,3 @@
fun f() {
val f: (Int) -> Int = { y -> y + y }
}
@@ -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<KtNameReferenceExpression>(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(