Better implementation of "Move lambda outside parenthesis"
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+5
-12
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -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 }
|
||||
}
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user