KT-5522 Rename refactoring on implicit lambda parameter 'it' should be allowed

#KT-5522 fixed
This commit is contained in:
Evgeny Gerashchenko
2014-07-30 21:55:48 +04:00
parent 8496c535aa
commit 670f135c75
6 changed files with 149 additions and 9 deletions
+1
View File
@@ -257,6 +257,7 @@
<renamePsiElementProcessor implementation="org.jetbrains.jet.plugin.refactoring.rename.RenameKotlinPropertyProcessor"
id="KotlinProperty"
order="first"/>
<renameHandler implementation="org.jetbrains.jet.plugin.refactoring.rename.RenameKotlinImplicitLambdaParameter"/>
<spellchecker.support implementationClass="org.jetbrains.jet.plugin.KotlinSpellcheckingStrategy" language="jet"/>
@@ -56,14 +56,7 @@ public class ReplaceItWithExplicitFunctionLiteralParamIntention() : PsiElementBa
override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean {
val simpleNameExpression = PsiTreeUtil.getParentOfType(element, javaClass<JetSimpleNameExpression>())
if (simpleNameExpression == null || simpleNameExpression.getReferencedName() != "it") {
return false
}
val bindingContext = AnalyzerFacadeWithCache.getContextForElement(simpleNameExpression)
val reference = simpleNameExpression.getReference() as JetReference?
val simpleNameTarget = reference?.resolveToDescriptors()?.firstOrNull() as? ValueParameterDescriptor?
if (simpleNameTarget == null || bindingContext.get(BindingContext.AUTO_CREATED_IT, simpleNameTarget) != true) {
if (simpleNameExpression == null || !isAutoCreatedIt(simpleNameExpression)) {
return false
}
@@ -74,4 +67,21 @@ public class ReplaceItWithExplicitFunctionLiteralParamIntention() : PsiElementBa
override fun getFamilyName(): String {
return JetBundle.message("replace.it.with.explicit.function.literal.param.family")
}
class object {
fun isAutoCreatedIt(simpleNameExpression: JetSimpleNameExpression): Boolean {
if (simpleNameExpression.getReferencedName() != "it") {
return false
}
val bindingContext = AnalyzerFacadeWithCache.getContextForElement(simpleNameExpression)
val reference = simpleNameExpression.getReference() as JetReference?
val simpleNameTarget = reference?.resolveToDescriptors()?.firstOrNull() as? ValueParameterDescriptor?
if (simpleNameTarget == null || bindingContext.get(BindingContext.AUTO_CREATED_IT, simpleNameTarget) != true) {
return false
}
return true
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2014 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.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.project.Project
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.plugin.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
public class RenameKotlinImplicitLambdaParameter: VariableInplaceRenameHandler() {
override fun isAvailable(element: PsiElement?, editor: Editor, file: PsiFile): Boolean {
val simpleNameExpression = PsiTreeUtil.findElementOfClassAtOffset(
file, editor.getCaretModel().getOffset(), javaClass<JetSimpleNameExpression>(), false)
return simpleNameExpression != null
&& ReplaceItWithExplicitFunctionLiteralParamIntention.isAutoCreatedIt(simpleNameExpression)
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
val intention = ReplaceItWithExplicitFunctionLiteralParamIntention()
CommandProcessor.getInstance().executeCommand(
project,
{ ApplicationManager.getApplication()!!.runWriteAction(Runnable {
intention.invoke(project, editor, file)
}) },
"Convert 'it' to explicit lambda parameter",
""
)
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
// Do nothing: this method is called not from editor
}
}
@@ -0,0 +1,3 @@
fun f() {
val f: (Int) -> Int = { <caret>it + it }
}
@@ -0,0 +1,3 @@
fun f() {
val f: (Int) -> Int = { y -> y + y }
}
@@ -30,8 +30,23 @@ import org.jetbrains.jet.plugin.PluginTestCaseBase
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.refactoring.rename.RenameHandler
import org.jetbrains.jet.plugin.refactoring.rename.RenameKotlinImplicitLambdaParameter
import com.intellij.openapi.project.Project
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.refactoring.rename.inplace.InplaceRefactoring
import com.intellij.injected.editor.EditorWindow
import com.intellij.codeInsight.template.impl.TemplateState
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.command.WriteCommandAction
public class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
{
System.setProperty("idea.platform.prefix", "Idea")
}
public class InplaceRenameTest : LightCodeInsightTestCase() {
override fun isRunInWriteAction(): Boolean = false
override fun getTestDataPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/refactoring/rename/inplace/"
@@ -51,6 +66,59 @@ public class InplaceRenameTest : LightCodeInsightTestCase() {
doTestInplaceRename("y")
}
public fun testFunctionLiteralIt() {
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.getCaretModel().getOffset())!!.getElement()
assertNotNull(element)
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.getName(), element!!,
LightPlatformCodeInsightTestCase.getCurrentEditorDataContext())
val handler = RenameKotlinImplicitLambdaParameter()
assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element)
val project = editor.getProject()!!
val templateManager = TemplateManager.getInstance(project) as TemplateManagerImpl
try {
templateManager.setTemplateTesting(true)
object : WriteCommandAction.Simple<Any>(project) {
throws(javaClass<Throwable>())
override fun run() {
handler.invoke(project, editor, file, dataContext)
}
}.execute()
var state = TemplateManagerImpl.getTemplateState(editor)
assert(state != null)
val range = state!!.getCurrentVariableRange()
assert(range != null)
object : WriteCommandAction.Simple<Any>(project) {
throws(javaClass<Throwable>())
override fun run() {
editor.getDocument().replaceString(range!!.getStartOffset(), range!!.getEndOffset(), newName)
}
}.execute().throwException()
state = TemplateManagerImpl.getTemplateState(editor)
assert(state != null)
state!!.gotoEnd(false)
}
finally {
templateManager.setTemplateTesting(false)
}
checkResultByFile(getTestName(false) + ".kt.after")
}
public fun testFunctionLiteralParenthesis() {
doTestInplaceRename("y")
}