diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index 83af6352ff1..bfea6fb75ea 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens @@ -43,7 +44,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.isError @Suppress("UNCHECKED_CAST") -inline fun PsiElement.replaced(newElement: T): T { +inline fun PsiElement.replaced(newElement: T): T { val result = replace(newElement) return if (result is T) result @@ -51,7 +52,8 @@ inline fun PsiElement.replaced(newElement: T): T { (result as KtParenthesizedExpression).expression as T } -@Suppress("UNCHECKED_CAST") fun T.copied(): T = copy() as T +@Suppress("UNCHECKED_CAST") +fun T.copied(): T = copy() as T fun KtLambdaArgument.moveInsideParentheses(bindingContext: BindingContext): KtCallExpression { return moveInsideParenthesesAndReplaceWith(this.getArgumentExpression(), bindingContext) @@ -70,7 +72,8 @@ fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith( val newCallExpression = oldCallExpression.copy() as KtCallExpression val psiFactory = KtPsiFactory(project) - val argument = if (newCallExpression.getValueArgumentsInParentheses().any { it.isNamed() }) { + + val argument = if (shouldLambdaParameterBeNamed(newCallExpression.getValueArgumentsInParentheses(), oldCallExpression)) { psiFactory.createArgument(replacement, functionLiteralArgumentName) } else { @@ -92,6 +95,13 @@ fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith( return oldCallExpression.replace(newCallExpression) as KtCallExpression } +private fun shouldLambdaParameterBeNamed(args: List, callExpr: KtCallExpression): Boolean { + if (args.any { it.isNamed() }) return true + val calee = (callExpr.calleeExpression?.mainReference?.resolve() as? KtFunction) ?: return true + return if (calee.valueParameters.any { it.isVarArg }) true else calee.valueParameters.size - 1 > args.size +} + + fun KtCallExpression.moveFunctionLiteralOutsideParentheses() { assert(lambdaArguments.isEmpty()) val argumentList = valueArgumentList!! @@ -169,7 +179,7 @@ fun PsiElement.deleteSingle() { CodeEditUtil.removeChild(parent?.node ?: return, node ?: return) } -fun KtClass.getOrCreateCompanionObject() : KtObjectDeclaration { +fun KtClass.getOrCreateCompanionObject(): KtObjectDeclaration { companionObjects.firstOrNull()?.let { return it } return addDeclaration(KtPsiFactory(this).createCompanionObject()) } @@ -354,11 +364,11 @@ fun KtTypeParameterListOwner.addTypeParameter(typeParameter: KtTypeParameter): K val list = KtPsiFactory(this).createTypeParameterList("") list.parameters[0].replace(typeParameter) val leftAnchor = when (this) { - is KtClass -> nameIdentifier ?: getClassOrInterfaceKeyword() - is KtNamedFunction -> funKeyword - is KtProperty -> valOrVarKeyword - else -> null - } ?: return null + is KtClass -> nameIdentifier ?: getClassOrInterfaceKeyword() + is KtNamedFunction -> funKeyword + is KtProperty -> valOrVarKeyword + else -> null + } ?: return null return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first() } diff --git a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt new file mode 100644 index 00000000000..bad41c393ae --- /dev/null +++ b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: true +fun foo() { + bar { + it * 3 + } +} + +fun bar(a : Int = 2, b: (Int) -> Int) { + b(a) +} diff --git a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt.after b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt.after new file mode 100644 index 00000000000..9896b5b18ec --- /dev/null +++ b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt.after @@ -0,0 +1,10 @@ +// IS_APPLICABLE: true +fun foo() { + bar(b = { + it * 3 + }) +} + +fun bar(a : Int = 2, b: (Int) -> Int) { + b(a) +} diff --git a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt new file mode 100644 index 00000000000..af1f0f9b929 --- /dev/null +++ b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: true +fun foo() { + bar(1) { + it * 3 + } +} + +fun bar(c: Int, a: Int = 2, b: (Int) -> Int) { + b(a) +} diff --git a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt.after b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt.after new file mode 100644 index 00000000000..5e5a9a81866 --- /dev/null +++ b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt.after @@ -0,0 +1,10 @@ +// IS_APPLICABLE: true +fun foo() { + bar(1, b = { + it * 3 + }) +} + +fun bar(c: Int, a: Int = 2, b: (Int) -> Int) { + b(a) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 7c9d37e975c..dc6e6f4ae00 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -11132,6 +11132,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("moveLambda13.kt") + public void testMoveLambda13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt"); + doTest(fileName); + } + + @TestMetadata("moveLambda14.kt") + public void testMoveLambda14() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt"); + doTest(fileName); + } + @TestMetadata("moveLambda2.kt") public void testMoveLambda2() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt");