diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index 04283e9c0aa..c5d3e827869 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -370,6 +370,7 @@ fun main(args: Array) { model("intentions/moveLambdaInsideParentheses", testMethod = "doTestMoveLambdaInsideParentheses") model("intentions/moveLambdaOutsideParentheses", testMethod = "doTestMoveLambdaOutsideParentheses") model("intentions/replaceExplicitFunctionLiteralParamWithIt", testMethod = "doTestReplaceExplicitFunctionLiteralParamWithIt") + model("intentions/replaceItWithExplicitFunctionLiteralParam", testMethod = "doTestReplaceItWithExplicitFunctionLiteralParam") } testClass(javaClass()) { diff --git a/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/after.kt.template b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/after.kt.template new file mode 100644 index 00000000000..9cab6176e61 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/after.kt.template @@ -0,0 +1 @@ +array(1, 2, 3).filter { x -> x % 2 == 0 } diff --git a/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/before.kt.template b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/before.kt.template new file mode 100644 index 00000000000..1ff32f3c4e2 --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/before.kt.template @@ -0,0 +1 @@ +array(1, 2, 3).filter { it % 2 == 0 } diff --git a/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/description.html b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/description.html new file mode 100644 index 00000000000..1962303ff2b --- /dev/null +++ b/idea/resources/intentionDescriptions/ReplaceItWithExplicitFunctionLiteralParamIntention/description.html @@ -0,0 +1,5 @@ + + + This intention replaces the implicit 'it' parameter in a function literal with an explicitly named parameter. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 18bc5805a8d..bc6c335d1b7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -533,6 +533,11 @@ Kotlin + + org.jetbrains.jet.plugin.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention + Kotlin + + diff --git a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties index b5f9f945e16..8f0efa94f84 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetBundle.properties +++ b/idea/src/org/jetbrains/jet/plugin/JetBundle.properties @@ -239,6 +239,8 @@ move.lambda.inside.parentheses=Move lambda function into parentheses move.lambda.inside.parentheses.family=Move lambda function into parentheses move.lambda.outside.parentheses=Move lambda expression out of parentheses move.lambda.outside.parentheses.family=Move lambda expression out of parentheses +replace.it.with.explicit.function.literal.param=Replace 'it' with explicit parameter +replace.it.with.explicit.function.literal.param.family=Replace 'it' With Explicit Parameter property.is.implemented.too.many=Has implementations property.is.overridden.too.many=Is overridden in subclasses diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt new file mode 100644 index 00000000000..1df55961d2b --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/intentions/ReplaceItWithExplicitFunctionLiteralParamIntention.kt @@ -0,0 +1,79 @@ +/* + * 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.intentions + +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler +import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression +import org.jetbrains.jet.lang.psi.JetPsiFactory +import org.jetbrains.jet.lang.resolve.BindingContext +import org.jetbrains.jet.plugin.JetBundle +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor +import org.jetbrains.jet.lang.resolve.BindingContextUtils +import org.jetbrains.jet.lang.psi.JetFunctionLiteral +import org.jetbrains.jet.plugin.references.JetReference +import org.jetbrains.jet.plugin.codeInsight.firstOrNull + +public class ReplaceItWithExplicitFunctionLiteralParamIntention() : PsiElementBaseIntentionAction() { + override fun invoke(project: Project, editor: Editor, element: PsiElement) { + val simpleNameExpression = PsiTreeUtil.getParentOfType(element, javaClass())!! + + val simpleNameReference = simpleNameExpression.getReference() as JetReference? + val target = simpleNameReference?.resolveToDescriptors()?.first()!! + + val bindingContext = AnalyzerFacadeWithCache.getContextForElement(simpleNameExpression) + val funcExpr = BindingContextUtils.descriptorToDeclaration(bindingContext, target.getContainingDeclaration()!!) as JetFunctionLiteral + + val newExpr = JetPsiFactory.createExpression(project, "{ it -> 42 }") as JetFunctionLiteralExpression + funcExpr.addRangeAfter(newExpr.getFunctionLiteral().getValueParameterList(), + newExpr.getFunctionLiteral().getArrowNode()!!.getPsi(), + funcExpr.getOpenBraceNode().getPsi()) + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()) + + val paramToRename = funcExpr.getValueParameters().first() + editor.getCaretModel().moveToOffset(paramToRename.getTextOffset()) + VariableInplaceRenameHandler().doRename(paramToRename, editor, null) + } + + 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) { + return false + } + + setText(JetBundle.message("replace.it.with.explicit.function.literal.param")) + return true + } + + override fun getFamilyName(): String { + return JetBundle.message("replace.it.with.explicit.function.literal.param.family") + } +} diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt new file mode 100644 index 00000000000..e0e27a8cf1b --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt @@ -0,0 +1,2 @@ +fun applyTwice(f: (A) -> A, x: A) = f(f(x)) +val x = applyTwice({ it + 1 }, 40) diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt.after b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt.after new file mode 100644 index 00000000000..32c9ecc013c --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt.after @@ -0,0 +1,2 @@ +fun applyTwice(f: (A) -> A, x: A) = f(f(x)) +val x = applyTwice({ it -> it + 1 }, 40) diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt new file mode 100644 index 00000000000..ef17aaaef12 --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt @@ -0,0 +1,2 @@ +fun foo(i: (Int) -> Int) = 0 +val x = foo { foo { x -> x + it } } diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt.after b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt.after new file mode 100644 index 00000000000..42ec3d827d6 --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt.after @@ -0,0 +1,2 @@ +fun foo(i: (Int) -> Int) = 0 +val x = foo { it -> foo { x -> x + it } } diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt new file mode 100644 index 00000000000..72c2b641589 --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt @@ -0,0 +1,2 @@ +fun foo(a: (Int) -> Int): Int = a(1) +val x = foo { it + foo { it } } \ No newline at end of file diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt.after b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt.after new file mode 100644 index 00000000000..3a530fc4654 --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt.after @@ -0,0 +1,2 @@ +fun foo(a: (Int) -> Int): Int = a(1) +val x = foo { it + foo { it -> it } } \ No newline at end of file diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_localVariableIt.kt b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_localVariableIt.kt new file mode 100644 index 00000000000..7c5296d3164 --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_localVariableIt.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(a: (Int) -> Int): Int = a(1) +val x = foo { + val it = 12 + it +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_parameterExplicitlyNamedIt.kt b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_parameterExplicitlyNamedIt.kt new file mode 100644 index 00000000000..cdee32367ff --- /dev/null +++ b/idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_parameterExplicitlyNamedIt.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: false +fun foo(i: (Int) -> Int) = 0 +val x = foo { it -> it + 1 } diff --git a/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java b/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java index 8a7659b27b7..32e54428714 100644 --- a/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/intentions/AbstractCodeTransformationTest.java @@ -146,6 +146,10 @@ public abstract class AbstractCodeTransformationTest extends LightCodeInsightTes doTestIntention(path, new ReplaceExplicitFunctionLiteralParamWithItIntention()); } + public void doTestReplaceItWithExplicitFunctionLiteralParam(@NotNull String path) throws Exception { + doTestIntention(path, new ReplaceItWithExplicitFunctionLiteralParamIntention()); + } + private void doTestIntention(@NotNull String path, @NotNull IntentionAction intentionAction) throws Exception { configureByFile(path); diff --git a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java index 0597d899a00..efea2539d93 100644 --- a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java @@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.intentions.AbstractCodeTransformationTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") -@InnerTestClasses({CodeTransformationTestGenerated.IfToAssignment.class, CodeTransformationTestGenerated.IfToReturn.class, CodeTransformationTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationTestGenerated.WhenToAssignment.class, CodeTransformationTestGenerated.WhenToReturn.class, CodeTransformationTestGenerated.AssignmentToIf.class, CodeTransformationTestGenerated.AssignmentToWhen.class, CodeTransformationTestGenerated.PropertyToIf.class, CodeTransformationTestGenerated.PropertyToWhen.class, CodeTransformationTestGenerated.ReturnToIf.class, CodeTransformationTestGenerated.ReturnToWhen.class, CodeTransformationTestGenerated.IfToWhen.class, CodeTransformationTestGenerated.WhenToIf.class, CodeTransformationTestGenerated.Flatten.class, CodeTransformationTestGenerated.Merge.class, CodeTransformationTestGenerated.IntroduceSubject.class, CodeTransformationTestGenerated.EliminateSubject.class, CodeTransformationTestGenerated.Split.class, CodeTransformationTestGenerated.Join.class, CodeTransformationTestGenerated.ConvertMemberToExtension.class, CodeTransformationTestGenerated.ReconstructedType.class, CodeTransformationTestGenerated.RemoveUnnecessaryParentheses.class, CodeTransformationTestGenerated.ReplaceWithDotQualifiedMethodCall.class, CodeTransformationTestGenerated.ReplaceWithInfixFunctionCall.class, CodeTransformationTestGenerated.RemoveCurlyBracesFromTemplate.class, CodeTransformationTestGenerated.MoveLambdaInsideParentheses.class, CodeTransformationTestGenerated.MoveLambdaOutsideParentheses.class, CodeTransformationTestGenerated.ReplaceExplicitFunctionLiteralParamWithIt.class}) +@InnerTestClasses({CodeTransformationTestGenerated.IfToAssignment.class, CodeTransformationTestGenerated.IfToReturn.class, CodeTransformationTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationTestGenerated.WhenToAssignment.class, CodeTransformationTestGenerated.WhenToReturn.class, CodeTransformationTestGenerated.AssignmentToIf.class, CodeTransformationTestGenerated.AssignmentToWhen.class, CodeTransformationTestGenerated.PropertyToIf.class, CodeTransformationTestGenerated.PropertyToWhen.class, CodeTransformationTestGenerated.ReturnToIf.class, CodeTransformationTestGenerated.ReturnToWhen.class, CodeTransformationTestGenerated.IfToWhen.class, CodeTransformationTestGenerated.WhenToIf.class, CodeTransformationTestGenerated.Flatten.class, CodeTransformationTestGenerated.Merge.class, CodeTransformationTestGenerated.IntroduceSubject.class, CodeTransformationTestGenerated.EliminateSubject.class, CodeTransformationTestGenerated.Split.class, CodeTransformationTestGenerated.Join.class, CodeTransformationTestGenerated.ConvertMemberToExtension.class, CodeTransformationTestGenerated.ReconstructedType.class, CodeTransformationTestGenerated.RemoveUnnecessaryParentheses.class, CodeTransformationTestGenerated.ReplaceWithDotQualifiedMethodCall.class, CodeTransformationTestGenerated.ReplaceWithInfixFunctionCall.class, CodeTransformationTestGenerated.RemoveCurlyBracesFromTemplate.class, CodeTransformationTestGenerated.MoveLambdaInsideParentheses.class, CodeTransformationTestGenerated.MoveLambdaOutsideParentheses.class, CodeTransformationTestGenerated.ReplaceExplicitFunctionLiteralParamWithIt.class, CodeTransformationTestGenerated.ReplaceItWithExplicitFunctionLiteralParam.class}) public class CodeTransformationTestGenerated extends AbstractCodeTransformationTest { @TestMetadata("idea/testData/intentions/branched/folding/ifToAssignment") public static class IfToAssignment extends AbstractCodeTransformationTest { @@ -1526,6 +1526,39 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT } + @TestMetadata("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam") + public static class ReplaceItWithExplicitFunctionLiteralParam extends AbstractCodeTransformationTest { + public void testAllFilesPresentInReplaceItWithExplicitFunctionLiteralParam() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("applicable.kt") + public void testApplicable() throws Exception { + doTestReplaceItWithExplicitFunctionLiteralParam("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable.kt"); + } + + @TestMetadata("applicable_nestedFunctionLiterals.kt") + public void testApplicable_nestedFunctionLiterals() throws Exception { + doTestReplaceItWithExplicitFunctionLiteralParam("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionLiterals.kt"); + } + + @TestMetadata("applicable_nestedFunctionWithIt.kt") + public void testApplicable_nestedFunctionWithIt() throws Exception { + doTestReplaceItWithExplicitFunctionLiteralParam("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/applicable_nestedFunctionWithIt.kt"); + } + + @TestMetadata("notApplicable_localVariableIt.kt") + public void testNotApplicable_localVariableIt() throws Exception { + doTestReplaceItWithExplicitFunctionLiteralParam("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_localVariableIt.kt"); + } + + @TestMetadata("notApplicable_parameterExplicitlyNamedIt.kt") + public void testNotApplicable_parameterExplicitlyNamedIt() throws Exception { + doTestReplaceItWithExplicitFunctionLiteralParam("idea/testData/intentions/replaceItWithExplicitFunctionLiteralParam/notApplicable_parameterExplicitlyNamedIt.kt"); + } + + } + public static Test suite() { TestSuite suite = new TestSuite("CodeTransformationTestGenerated"); suite.addTestSuite(IfToAssignment.class); @@ -1556,6 +1589,7 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT suite.addTestSuite(MoveLambdaInsideParentheses.class); suite.addTestSuite(MoveLambdaOutsideParentheses.class); suite.addTestSuite(ReplaceExplicitFunctionLiteralParamWithIt.class); + suite.addTestSuite(ReplaceItWithExplicitFunctionLiteralParam.class); return suite; } }