Refactor: Move canMoveLambdaOutsideParentheses() function to idea-core module

This commit is contained in:
Alexey Sedunov
2018-02-06 19:09:15 +03:00
parent 48705865bd
commit fffd74f002
3 changed files with 42 additions and 38 deletions

View File

@@ -21,6 +21,7 @@ import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -100,6 +101,32 @@ private fun shouldLambdaParameterBeNamed(args: List<ValueArgument>, callExpr: Kt
return if (calee.valueParameters.any { it.isVarArg }) true else calee.valueParameters.size - 1 > args.size
}
fun KtCallExpression.getLastLambdaExpression(): KtLambdaExpression? {
if (lambdaArguments.isNotEmpty()) return null
return valueArguments.lastOrNull()?.getArgumentExpression()?.unpackFunctionLiteral()
}
fun KtCallExpression.canMoveLambdaOutsideParentheses(): Boolean {
if (getLastLambdaExpression() == null) return false
val callee = calleeExpression
if (callee is KtNameReferenceExpression) {
val bindingContext = analyze(BodyResolveMode.PARTIAL)
val targets = bindingContext[BindingContext.REFERENCE_TARGET, callee]?.let { listOf(it) }
?: bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, callee]
?: listOf()
val candidates = targets.filterIsInstance<FunctionDescriptor>()
// if there are functions among candidates but none of them have last function parameter then not show the intention
if (candidates.isNotEmpty() && candidates.none {
val lastParameter = it.valueParameters.lastOrNull()
lastParameter != null && lastParameter.type.isFunctionType
}) {
return false
}
}
return true
}
fun KtCallExpression.moveFunctionLiteralOutsideParentheses() {
assert(lambdaArguments.isEmpty())

View File

@@ -17,58 +17,31 @@
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.KtValueArgumentList
import org.jetbrains.kotlin.psi.psiUtil.containsInside
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class MoveLambdaOutsideParenthesesIntention : SelfTargetingIntention<KtCallExpression>(KtCallExpression::class.java, "Move lambda argument out of parentheses") {
companion object {
private fun getLambdaExpression(callExpression: KtCallExpression): KtLambdaExpression? {
if (callExpression.lambdaArguments.isNotEmpty()) return null
return callExpression.valueArguments.lastOrNull()?.getArgumentExpression()?.unpackFunctionLiteral()
}
fun canMove(element: KtCallExpression): Boolean {
if (getLambdaExpression(element) == null) return false
val callee = element.calleeExpression
if (callee is KtNameReferenceExpression) {
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val targets = bindingContext[BindingContext.REFERENCE_TARGET, callee]?.let { listOf(it) }
?: bindingContext[BindingContext.AMBIGUOUS_REFERENCE_TARGET, callee]
?: listOf()
val candidates = targets.filterIsInstance<FunctionDescriptor>()
// if there are functions among candidates but none of them have last function parameter then not show the intention
if (candidates.isNotEmpty() && candidates.none {
val lastParameter = it.valueParameters.lastOrNull()
lastParameter != null && lastParameter.type.isFunctionType
}) {
return false
}
}
return true
}
fun moveFunctionLiteralOutsideParenthesesIfPossible(expression: KtLambdaExpression) {
val call = ((expression.parent as? KtValueArgument)?.parent as? KtValueArgumentList)?.parent as? KtCallExpression ?: return
if (canMove(call)) {
if (call.canMoveLambdaOutsideParentheses()) {
call.moveFunctionLiteralOutsideParentheses()
}
}
}
override fun isApplicableTo(element: KtCallExpression, caretOffset: Int): Boolean {
if (!canMove(element)) return false
if (!element.canMoveLambdaOutsideParentheses()) return false
val lambdaExpression = getLambdaExpression(element) ?: return false
val lambdaExpression = element.getLastLambdaExpression() ?: return false
val argument = lambdaExpression.getStrictParentOfType<KtValueArgument>() ?: return false
if (caretOffset < argument.startOffset) return false
val bodyRange = lambdaExpression.bodyExpression?.textRange ?: return true

View File

@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
@@ -36,7 +37,10 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.contentRange
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -152,7 +156,7 @@ class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention<KtObjectLiter
?.parent as? KtCallExpression
if (parentCall != null && RedundantSamConstructorInspection.samConstructorCallsToBeConverted(parentCall).singleOrNull() == callExpression) {
RedundantSamConstructorInspection.replaceSamConstructorCall(callExpression)
if (MoveLambdaOutsideParenthesesIntention.canMove(parentCall)) {
if (parentCall.canMoveLambdaOutsideParentheses()) {
parentCall.moveFunctionLiteralOutsideParentheses()
}
}