Implement intention to add labeled return to last expression in a lambda

So #KT-20439 Fixed
This commit is contained in:
shiraji
2018-01-07 22:05:44 +03:00
committed by Mikhail Glukhikh
parent 769e28519e
commit 59f6dc07cf
26 changed files with 311 additions and 11 deletions
+5
View File
@@ -1562,6 +1562,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
class AddLabeledReturnInLambdaIntention : SelfTargetingRangeIntention<KtLambdaExpression>(
KtLambdaExpression::class.java,
"Add labeled return to last expression in a lambda"
), LowPriorityAction {
override fun applicabilityRange(element: KtLambdaExpression): TextRange? {
if (!isApplicableTo(element)) return null
val labelName = createLabelName(element) ?: return null
text = "Add return@$labelName"
return element.bodyExpression?.statements?.last()?.textRange
}
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
if (!isApplicableTo(element)) return
val labelName = createLabelName(element) ?: return
val lastStatement = element.bodyExpression?.statements?.last() ?: return
val newExpression = KtPsiFactory(element.project).createExpressionByPattern("return@$labelName $0", lastStatement)
lastStatement.replace(newExpression)
}
private fun isApplicableTo(element: KtLambdaExpression): Boolean {
val block = element.bodyExpression ?: return false
val lastStatement = block.statements.last()
return lastStatement !is KtReturnExpression && lastStatement.isUsedAsExpression(lastStatement.analyze())
}
private fun createLabelName(element: KtLambdaExpression): String? {
val block = element.bodyExpression ?: return null
val callExpression = element.getStrictParentOfType<KtCallExpression>() ?: return null
val valueArgument = callExpression.valueArguments.findArgumentWithGivenBlock(block) ?: return null
val lambdaLabelName = (valueArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
return lambdaLabelName ?: callExpression.getCallNameExpression()?.text
}
}
@@ -9,7 +9,6 @@ import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpression>(
@@ -21,15 +20,7 @@ class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpr
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
if (block.statements.lastOrNull() != element) return false
val callExpression = block.getStrictParentOfType<KtCallExpression>() ?: return false
val lambdaArgument = callExpression.lambdaArguments.firstOrNull {
val argumentExpression = it.getArgumentExpression()
val lambda = when (argumentExpression) {
is KtLambdaExpression -> argumentExpression
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
else -> null
}
lambda?.bodyExpression === block
} ?: return false
val lambdaArgument = callExpression.lambdaArguments.findArgumentWithGivenBlock(block) ?: return false
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
?: callExpression.getCallNameExpression()?.text ?: return false
if (labelName != callName) return false
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -295,3 +294,15 @@ fun KtCallExpression.isArrayOfMethod(): Boolean {
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
ARRAY_OF_METHODS.contains(descriptor.name)
}
fun <T : KtValueArgument> List<T>.findArgumentWithGivenBlock(
block: KtBlockExpression
): T? = firstOrNull {
val argumentExpression = it.getArgumentExpression()
val lambda = when (argumentExpression) {
is KtLambdaExpression -> argumentExpression
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
else -> null
}
lambda?.bodyExpression === block
}