Better implementation of "Move lambda outside parenthesis"

This commit is contained in:
Valentin Kipyatkov
2015-05-22 13:54:52 +03:00
parent 3bca1d1f83
commit d7c64d44b6
10 changed files with 57 additions and 49 deletions
@@ -21,19 +21,18 @@ import com.intellij.psi.PsiWhiteSpace
public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node), FunctionLiteralArgument {
private fun assertFL() = throw AssertionError("Function literal argument doesn't contain function literal expression: " +
"${super<JetValueArgument>.getArgumentExpression()?.getText()} (it should be guaranteed by parser)")
override fun getArgumentExpression() = super<JetValueArgument>.getArgumentExpression()!!
override fun getArgumentExpression() = super<JetValueArgument>.getArgumentExpression() ?: assertFL()
override fun getFunctionLiteral(): JetFunctionLiteralExpression = unpackFunctionLiteral(getArgumentExpression())
private fun unpackFunctionLiteral(expression: JetExpression?): JetFunctionLiteralExpression =
when (expression) {
is JetFunctionLiteralExpression -> expression
is JetLabeledExpression -> unpackFunctionLiteral(expression.getBaseExpression())
is JetAnnotatedExpression -> unpackFunctionLiteral(expression.getBaseExpression())
else -> assertFL()
}
override fun getFunctionLiteral(): JetFunctionLiteralExpression = getArgumentExpression().unpackFunctionLiteral()!!
}
public fun JetExpression.unpackFunctionLiteral(): JetFunctionLiteralExpression? {
return when (this) {
is JetFunctionLiteralExpression -> this
is JetLabeledExpression -> getBaseExpression()?.unpackFunctionLiteral()
is JetAnnotatedExpression -> getBaseExpression()?.unpackFunctionLiteral()
else -> null
}
}
@@ -95,7 +95,10 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args:
CodeEditUtil.setNodeGeneratedRecursively(expression.getNode(), false)
for ((pointer, n) in pointers) {
val element = pointer.getElement()!!
var element = pointer.getElement()!!
if (element is JetFunctionLiteral) {
element = element.getParent() as JetFunctionLiteralExpression
}
element.replace(args[n] as PsiElement)
}
@@ -20,7 +20,7 @@ import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
@@ -29,9 +29,10 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention<JetCallExpression>(javaClass(), "Move lambda argument out of parentheses") {
override fun isApplicableTo(element: JetCallExpression, caretOffset: Int): Boolean {
val argument = element.getValueArgumentsInParentheses().lastOrNull() ?: return false
if (element.getFunctionLiteralArguments().isNotEmpty()) return false
val argument = element.getValueArguments().lastOrNull() ?: return false
val expression = argument.getArgumentExpression() ?: return false
val functionLiteral = getFunctionLiteral(expression) ?: return false
val functionLiteral = expression.unpackFunctionLiteral() ?: return false
val callee = element.getCalleeExpression()
if (callee is JetSimpleNameExpression) {
@@ -54,15 +55,7 @@ public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention<J
return !bodyRange.containsInside(caretOffset)
}
private fun getFunctionLiteral(expression: JetExpression?): JetFunctionLiteral? {
return when (expression) {
is JetFunctionLiteralExpression -> expression.getFunctionLiteral()
is JetLabeledExpression -> getFunctionLiteral(expression.getBaseExpression())
else -> null
}
}
override fun applyTo(element: JetCallExpression, editor: Editor) {
element.moveLambdaOutsideParentheses()
element.moveFunctionLiteralOutsideParentheses()
}
}
@@ -391,8 +391,9 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
element.deleteChildRange(KotlinPackage.first(lambdaArguments), KotlinPackage.last(lambdaArguments));
}
//TODO: this is not correct!
JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArgumentList.getArguments());
boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && lastArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression;
boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && PsiPackage.unpackFunctionLiteral(lastArgument.getArgumentExpression()) != null;
arguments = (JetValueArgumentList) arguments.replace(newArgumentList);
@@ -430,11 +431,11 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
}
if (hasTrailingLambdaInArgumentListAfter) {
JetCallElement newCallElement =
(JetCallElement) (newElement instanceof JetQualifiedExpression
JetCallExpression newCallExpression =
(JetCallExpression) (newElement instanceof JetQualifiedExpression
? ((JetQualifiedExpression) newElement).getSelectorExpression()
: newElement);
PsiModificationUtilPackage.moveLambdaOutsideParentheses(newCallElement);
PsiModificationUtilPackage.moveFunctionLiteralOutsideParentheses(newCallExpression);
}
}
@@ -54,7 +54,7 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
psiFactory.createArgument(replacement)
}
val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().head!!
val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().firstOrNull()!!
val valueArgumentList = newCallExpression.getValueArgumentList() ?: psiFactory.createCallArguments("()")
valueArgumentList.addArgument(argument)
@@ -69,20 +69,20 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith(
return oldCallExpression.replace(newCallExpression) as JetCallExpression
}
fun JetCallElement.moveLambdaOutsideParentheses() {
val args = getValueArgumentsInParentheses()
val functionLiteral = args.last!!.getArgumentExpression()?.getText()
val calleeText = getCalleeExpression()?.getText()
if (calleeText == null || functionLiteral == null) return
fun JetCallExpression.moveFunctionLiteralOutsideParentheses() {
assert(getFunctionLiteralArguments().isEmpty())
val argumentList = getValueArgumentList()!!
val argument = argumentList.getArguments().last()
val expression = argument.getArgumentExpression()!!
assert(expression.unpackFunctionLiteral() != null)
val params = args.subList(0, args.size - 1).map { it.asElement().getText() ?: "" }.joinToString(", ", "(", ")")
val newCall =
if (params == "()") {
"$calleeText $functionLiteral"
}
else {
"$calleeText$params $functionLiteral"
}
replace(JetPsiFactory(this).createExpression(newCall))
val dummyCall = JetPsiFactory(this).createExpressionByPattern("foo()$0={}$", expression) as JetCallExpression
val functionLiteralArgument = dummyCall.getFunctionLiteralArguments().single()
this.add(functionLiteralArgument)
if (argumentList.getArguments().size() > 1) {
argumentList.removeArgument(argument)
}
else {
argumentList.delete()
}
}
@@ -3,7 +3,7 @@
// ERROR: Unresolved reference: it
fun foo() {
bar <caret>{ it }
bar<caret> { it }
}
fun bar(a: Int = 0, f: (Int) -> Int) { }
@@ -1,3 +1,3 @@
fun foo(p: (Int, () -> Int) -> Unit) {
p(1) <caret>{ 2 }
p(1<caret>) { 2 }
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
fun foo() {
bar(<caret>{ it }) {it}
}
fun bar(p1: (Int) -> Int, p2: (Int) -> Int) { }
@@ -3,7 +3,7 @@ fun bar(a: Int, b: Int, f: () -> Unit) {}
fun foo(a: Int, b: Int) = 2
fun test() {
bar(1, 2) {
bar(1, 2 /* , , */) {
val a = foo(1, 2)
}
}
@@ -4951,6 +4951,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("inapplicableAlreadyHasFunctionLiteral.kt")
public void testInapplicableAlreadyHasFunctionLiteral() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt");
doTest(fileName);
}
@TestMetadata("inapplicableOptionalParametersAfter.kt")
public void testInapplicableOptionalParametersAfter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableOptionalParametersAfter.kt");