From a7ac01ee6f6dedcc14f9db9024f5efecea874a05 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Thu, 3 Mar 2016 23:48:29 +0100 Subject: [PATCH 1/4] Implement Smart Enter Processor for the last function call argument being a lambda Fixes #KT-6785 --- .../idea/editor/KotlinSmartEnterHandler.kt | 4 +- .../fixers/KotlinLastLambdaParameterFixer.kt | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinSmartEnterHandler.kt b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinSmartEnterHandler.kt index 2b9f39b06a7..f319cad6d52 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinSmartEnterHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinSmartEnterHandler.kt @@ -54,7 +54,9 @@ class KotlinSmartEnterHandler: SmartEnterProcessorWithFixers() { KotlinTryBodyFixer(), KotlinCatchParameterFixer(), KotlinCatchBodyFixer(), - KotlinFinallyBodyFixer() + KotlinFinallyBodyFixer(), + + KotlinLastLambdaParameterFixer() ) addEnterProcessors(KotlinPlainEnterProcessor()) diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt new file mode 100644 index 00000000000..1fe521fa308 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.editor.fixers + +import com.intellij.lang.SmartEnterProcessorWithFixers +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.editor.KotlinSmartEnterHandler +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class KotlinLastLambdaParameterFixer : SmartEnterProcessorWithFixers.Fixer() { + override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) { + if (element !is KtCallExpression) return + + val bindingContext = element.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = element.getResolvedCall(bindingContext) ?: return + + val valueParameters = resolvedCall.candidateDescriptor.valueParameters + + if (resolvedCall.valueArguments.size == valueParameters.size - 1) { + val type = valueParameters.last().type + if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) { + val doc = editor.document + + var offset = element.endOffset + if (element.valueArgumentList?.rightParenthesis == null) { + doc.insertString(offset, ")") + offset++ + } + + var arity = type.constructor.parameters.size - 1 + if (KotlinBuiltIns.isExactExtensionFunctionType(type)) arity-- + + if (arity <= 1) { + doc.insertString(offset, "{\n}") + } else { + val params = (1..arity).map { "x$it" }.joinToString(", ") + doc.insertString(offset, "{ $params ->\n}") + } + } + } + } +} \ No newline at end of file From 12237c3cbaa633d21c1c4ebb024b9e7c5509333d Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Sat, 5 Mar 2016 00:49:26 +0100 Subject: [PATCH 2/4] test lambda parameter smart enter processor --- .../codeInsight/smartEnter/SmartEnterTest.kt | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt index 761f5193d04..9ed91ef1c09 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt @@ -1085,6 +1085,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { } """ ) + fun testSetter6() = doFileTest( """ var a : Int = 0 @@ -1263,6 +1264,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { } """ ) + fun testFinallyBody() = doFunTest( """ try { @@ -1279,6 +1281,126 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { """ ) + fun testLambdaParamImplicit1() = doFileTest( + """ + fun foo(a: Any, block: () -> Unit) { + } + fun test() { + foo(Any()) + } + """ + , + """ + fun foo(a: Any, block: () -> Unit) { + } + fun test() { + foo(Any()) { + + } + } + """ + ) + + fun testLambdaParamImplicit2() = doFileTest( + """ + fun foo(a: Any, block: (Any) -> Unit) { + } + fun test() { + foo(Any() + } + """ + , + """ + fun foo(a: Any, block: (Any) -> Unit) { + } + fun test() { + foo(Any()) { + + } + } + """ + ) + + fun testLambdaParamExplicit1() = doFileTest( + """ + fun foo(a: Any, block: (Any, Any) -> Unit) { + } + fun test() { + foo(Any()) + } + """ + , + """ + fun foo(a: Any, block: (Any, Any) -> Unit) { + } + fun test() { + foo(Any()) { x1, x2 -> + + } + } + """ + ) + + fun testExtensionLambdaParamImplicit1() = doFileTest( + """ + fun foo(a: Any, block: Any.() -> Unit) { + } + fun test() { + foo(Any()) + } + """ + , + """ + fun foo(a: Any, block: Any.() -> Unit) { + } + fun test() { + foo(Any()) { + + } + } + """ + ) + + fun testExtensionLambdaParamImplicit2() = doFileTest( + """ + fun foo(a: Any, block: Any.(Any) -> Unit) { + } + fun test() { + foo(Any() + } + """ + , + """ + fun foo(a: Any, block: Any.(Any) -> Unit) { + } + fun test() { + foo(Any()) { + + } + } + """ + ) + + fun testExtensionLambdaParamExplicit1() = doFileTest( + """ + fun foo(a: Any, block: Any.(Any, Any) -> Unit) { + } + fun test() { + foo(Any()) + } + """ + , + """ + fun foo(a: Any, block: Any.(Any, Any) -> Unit) { + } + fun test() { + foo(Any()) { x1, x2 -> + + } + } + """ + ) + fun doFunTest(before: String, after: String) { fun String.withFunContext(): String { val bodyText = "//----\n${this.trimIndent()}\n//----" From f7af927e8f35aa92488cf5bf21df2f65152cba58 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Tue, 8 Mar 2016 22:44:13 +0100 Subject: [PATCH 3/4] Don't set parameter names and register unresolved error correctly --- .../fixers/KotlinLastLambdaParameterFixer.kt | 29 +++--- .../codeInsight/smartEnter/SmartEnterTest.kt | 90 +------------------ 2 files changed, 22 insertions(+), 97 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt index 1fe521fa308..0883303316d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt @@ -19,9 +19,11 @@ package org.jetbrains.kotlin.idea.editor.fixers import com.intellij.lang.SmartEnterProcessorWithFixers import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.editor.KotlinSmartEnterHandler +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -29,8 +31,15 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode class KotlinLastLambdaParameterFixer : SmartEnterProcessorWithFixers.Fixer() { override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) { - if (element !is KtCallExpression) return + if (element is KtCallExpression) { + addBracesIfNecessary(editor, element, processor) + } + else if (element is LeafPsiElement) { + registerErrorIfNecessary(element, processor) + } + } + private fun addBracesIfNecessary(editor: Editor, element: KtCallExpression, processor: KotlinSmartEnterHandler) { val bindingContext = element.analyze(BodyResolveMode.PARTIAL) val resolvedCall = element.getResolvedCall(bindingContext) ?: return @@ -47,16 +56,16 @@ class KotlinLastLambdaParameterFixer : SmartEnterProcessorWithFixers.Fixer\n}") - } + doc.insertString(offset, "{ }") + processor.registerUnresolvedError(offset + 2) } } } + + private fun registerErrorIfNecessary(element: LeafPsiElement, processor: KotlinSmartEnterHandler) { + if(element.elementType != KtTokens.LBRACE) return + if(element.parent?.parent?.parent?.parent !is KtCallExpression) return + + processor.registerUnresolvedError(element.endOffset + 1) + } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt index 9ed91ef1c09..6b68cf7f1b8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/smartEnter/SmartEnterTest.kt @@ -1281,7 +1281,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { """ ) - fun testLambdaParamImplicit1() = doFileTest( + fun testLambdaParam() = doFileTest( """ fun foo(a: Any, block: () -> Unit) { } @@ -1294,49 +1294,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { fun foo(a: Any, block: () -> Unit) { } fun test() { - foo(Any()) { - - } - } - """ - ) - - fun testLambdaParamImplicit2() = doFileTest( - """ - fun foo(a: Any, block: (Any) -> Unit) { - } - fun test() { - foo(Any() - } - """ - , - """ - fun foo(a: Any, block: (Any) -> Unit) { - } - fun test() { - foo(Any()) { - - } - } - """ - ) - - fun testLambdaParamExplicit1() = doFileTest( - """ - fun foo(a: Any, block: (Any, Any) -> Unit) { - } - fun test() { - foo(Any()) - } - """ - , - """ - fun foo(a: Any, block: (Any, Any) -> Unit) { - } - fun test() { - foo(Any()) { x1, x2 -> - - } + foo(Any()) { } } """ ) @@ -1354,49 +1312,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { fun foo(a: Any, block: Any.() -> Unit) { } fun test() { - foo(Any()) { - - } - } - """ - ) - - fun testExtensionLambdaParamImplicit2() = doFileTest( - """ - fun foo(a: Any, block: Any.(Any) -> Unit) { - } - fun test() { - foo(Any() - } - """ - , - """ - fun foo(a: Any, block: Any.(Any) -> Unit) { - } - fun test() { - foo(Any()) { - - } - } - """ - ) - - fun testExtensionLambdaParamExplicit1() = doFileTest( - """ - fun foo(a: Any, block: Any.(Any, Any) -> Unit) { - } - fun test() { - foo(Any()) - } - """ - , - """ - fun foo(a: Any, block: Any.(Any, Any) -> Unit) { - } - fun test() { - foo(Any()) { x1, x2 -> - - } + foo(Any()) { } } """ ) From 828a7de9808f5c91ab71df22a1669331f4e8fcf8 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Wed, 9 Mar 2016 12:35:48 +0100 Subject: [PATCH 4/4] Simplify logic for last lambda fixer and set caret directly in first pass --- .../fixers/KotlinLastLambdaParameterFixer.kt | 21 +++---------------- .../codeInsight/smartEnter/SmartEnterTest.kt | 6 +++--- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt index 0883303316d..ef8107f50c3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/editor/fixers/KotlinLastLambdaParameterFixer.kt @@ -19,11 +19,9 @@ package org.jetbrains.kotlin.idea.editor.fixers import com.intellij.lang.SmartEnterProcessorWithFixers import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiElement -import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.editor.KotlinSmartEnterHandler -import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -31,15 +29,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode class KotlinLastLambdaParameterFixer : SmartEnterProcessorWithFixers.Fixer() { override fun apply(editor: Editor, processor: KotlinSmartEnterHandler, element: PsiElement) { - if (element is KtCallExpression) { - addBracesIfNecessary(editor, element, processor) - } - else if (element is LeafPsiElement) { - registerErrorIfNecessary(element, processor) - } - } + if (element !is KtCallExpression) return - private fun addBracesIfNecessary(editor: Editor, element: KtCallExpression, processor: KotlinSmartEnterHandler) { val bindingContext = element.analyze(BodyResolveMode.PARTIAL) val resolvedCall = element.getResolvedCall(bindingContext) ?: return @@ -56,16 +47,10 @@ class KotlinLastLambdaParameterFixer : SmartEnterProcessorWithFixers.Fixer Unit) { } fun test() { - foo(Any()) { } + foo(Any()) { } } """ ) - fun testExtensionLambdaParamImplicit1() = doFileTest( + fun testExtensionLambdaParam() = doFileTest( """ fun foo(a: Any, block: Any.() -> Unit) { } @@ -1312,7 +1312,7 @@ class SmartEnterTest : KotlinLightCodeInsightFixtureTestCase() { fun foo(a: Any, block: Any.() -> Unit) { } fun test() { - foo(Any()) { } + foo(Any()) { } } """ )