diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index b573a15f644..79e33973571 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -257,6 +257,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt index c105eb2b637..27879050e42 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt @@ -56,14 +56,7 @@ public class ReplaceItWithExplicitFunctionLiteralParamIntention() : PsiElementBa override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean { val simpleNameExpression = PsiTreeUtil.getParentOfType(element, javaClass()) - 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 + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt new file mode 100644 index 00000000000..a40ad97d52e --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinImplicitLambdaParameter.kt @@ -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(), 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, dataContext: DataContext?) { + // Do nothing: this method is called not from editor + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/FunctionLiteralIt.kt b/idea/testData/refactoring/rename/inplace/FunctionLiteralIt.kt new file mode 100644 index 00000000000..7f48570e510 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/FunctionLiteralIt.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/FunctionLiteralIt.kt.after b/idea/testData/refactoring/rename/inplace/FunctionLiteralIt.kt.after new file mode 100644 index 00000000000..9dab32c0735 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/FunctionLiteralIt.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/jet/plugin/refactoring/InplaceRenameTest.kt b/idea/tests/org/jetbrains/jet/plugin/refactoring/InplaceRenameTest.kt index 068f2445530..9d154b13531 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/InplaceRenameTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/InplaceRenameTest.kt @@ -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(project) { + throws(javaClass()) + 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(project) { + throws(javaClass()) + 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") }