From 0e5603f6440bf2da15cf92e8e82582bd93e123cd Mon Sep 17 00:00:00 2001 From: shiraji Date: Sat, 4 Feb 2017 09:03:39 +0900 Subject: [PATCH] Implement an intention converting several calls with same receiver to with/apply/run #KT-12183 Fixed --- .../ConvertToApplyIntention/after.kt.template | 7 + .../before.kt.template | 6 + .../ConvertToApplyIntention/description.html | 5 + .../ConvertToRunIntention/after.kt.template | 7 + .../ConvertToRunIntention/before.kt.template | 5 + .../ConvertToRunIntention/description.html | 5 + .../ConvertToWithIntention/after.kt.template | 7 + .../ConvertToWithIntention/before.kt.template | 5 + .../ConvertToWithIntention/description.html | 5 + idea/src/META-INF/plugin.xml | 15 ++ .../ConvertDotQualifiedToScopeIntention.kt | 40 ++++ .../intentions/ConvertToApplyIntention.kt | 80 +++++++ .../idea/intentions/ConvertToRunIntention.kt | 28 +++ .../intentions/ConvertToScopeIntention.kt | 99 ++++++++ .../idea/intentions/ConvertToWithIntention.kt | 27 +++ .../intentions/convertToApply/.intention | 1 + .../intentions/convertToApply/methodChain.kt | 14 ++ .../convertToApply/methodChain.kt.after | 15 ++ .../methodChainWithItParameter.kt | 17 ++ .../methodChainWithThisParameter.kt | 15 ++ .../intentions/convertToApply/normal.kt | 14 ++ .../intentions/convertToApply/normal.kt.after | 15 ++ .../intentions/convertToApply/normal2.kt | 14 ++ .../convertToApply/normal2.kt.after | 15 ++ .../intentions/convertToApply/normal3.kt | 14 ++ .../convertToApply/normal3.kt.after | 15 ++ .../convertToApply/thisParameter.kt | 15 ++ .../convertToApply/thisParameter2.kt | 15 ++ .../convertToApply/untilThisParameter.kt | 15 ++ .../untilThisParameter.kt.after | 16 ++ .../testData/intentions/convertToApply/var.kt | 14 ++ .../intentions/convertToApply/var.kt.after | 15 ++ .../convertToApply/withCommentAndSpaces.kt | 20 ++ .../withCommentAndSpaces.kt.after | 21 ++ .../intentions/convertToRun/.intention | 1 + .../intentions/convertToRun/itReceiver.kt | 16 ++ .../intentions/convertToRun/methodChain.kt | 13 + .../convertToRun/methodChain.kt.after | 15 ++ .../methodChainWithItParameter.kt | 15 ++ .../methodChainWithThisParameter.kt | 13 + .../intentions/convertToRun/normal.kt | 13 + .../intentions/convertToRun/normal.kt.after | 15 ++ .../intentions/convertToRun/normal2.kt | 13 + .../intentions/convertToRun/normal2.kt.after | 15 ++ .../intentions/convertToRun/normal3.kt | 13 + .../intentions/convertToRun/normal3.kt.after | 15 ++ .../intentions/convertToRun/thisParameter.kt | 14 ++ .../intentions/convertToRun/thisReceiver.kt | 14 ++ .../convertToRun/untilThisParameter.kt | 14 ++ .../convertToRun/untilThisParameter.kt.after | 16 ++ .../convertToRun/withCommentAndSpaces.kt | 18 ++ .../withCommentAndSpaces.kt.after | 20 ++ .../intentions/convertToWith/.intention | 1 + .../intentions/convertToWith/itReceiver.kt | 16 ++ .../intentions/convertToWith/methodChain.kt | 13 + .../convertToWith/methodChain.kt.after | 15 ++ .../methodChainWithItParameter.kt | 15 ++ .../methodChainWithThisParameter.kt | 13 + .../intentions/convertToWith/normal.kt | 13 + .../intentions/convertToWith/normal.kt.after | 15 ++ .../intentions/convertToWith/normal2.kt | 13 + .../intentions/convertToWith/normal2.kt.after | 15 ++ .../intentions/convertToWith/normal3.kt | 13 + .../intentions/convertToWith/normal3.kt.after | 15 ++ .../intentions/convertToWith/thisParameter.kt | 14 ++ .../intentions/convertToWith/thisReceiver.kt | 14 ++ .../convertToWith/untilThisParameter.kt | 14 ++ .../convertToWith/untilThisParameter.kt.after | 16 ++ .../convertToWith/withCommentAndSpaces.kt | 18 ++ .../withCommentAndSpaces.kt.after | 20 ++ .../intentions/IntentionTestGenerated.java | 225 ++++++++++++++++++ 71 files changed, 1352 insertions(+) create mode 100644 idea/resources/intentionDescriptions/ConvertToApplyIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToApplyIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToApplyIntention/description.html create mode 100644 idea/resources/intentionDescriptions/ConvertToRunIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToRunIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToRunIntention/description.html create mode 100644 idea/resources/intentionDescriptions/ConvertToWithIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToWithIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertToWithIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertDotQualifiedToScopeIntention.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToApplyIntention.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToRunIntention.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToScopeIntention.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToWithIntention.kt create mode 100644 idea/testData/intentions/convertToApply/.intention create mode 100644 idea/testData/intentions/convertToApply/methodChain.kt create mode 100644 idea/testData/intentions/convertToApply/methodChain.kt.after create mode 100644 idea/testData/intentions/convertToApply/methodChainWithItParameter.kt create mode 100644 idea/testData/intentions/convertToApply/methodChainWithThisParameter.kt create mode 100644 idea/testData/intentions/convertToApply/normal.kt create mode 100644 idea/testData/intentions/convertToApply/normal.kt.after create mode 100644 idea/testData/intentions/convertToApply/normal2.kt create mode 100644 idea/testData/intentions/convertToApply/normal2.kt.after create mode 100644 idea/testData/intentions/convertToApply/normal3.kt create mode 100644 idea/testData/intentions/convertToApply/normal3.kt.after create mode 100644 idea/testData/intentions/convertToApply/thisParameter.kt create mode 100644 idea/testData/intentions/convertToApply/thisParameter2.kt create mode 100644 idea/testData/intentions/convertToApply/untilThisParameter.kt create mode 100644 idea/testData/intentions/convertToApply/untilThisParameter.kt.after create mode 100644 idea/testData/intentions/convertToApply/var.kt create mode 100644 idea/testData/intentions/convertToApply/var.kt.after create mode 100644 idea/testData/intentions/convertToApply/withCommentAndSpaces.kt create mode 100644 idea/testData/intentions/convertToApply/withCommentAndSpaces.kt.after create mode 100644 idea/testData/intentions/convertToRun/.intention create mode 100644 idea/testData/intentions/convertToRun/itReceiver.kt create mode 100644 idea/testData/intentions/convertToRun/methodChain.kt create mode 100644 idea/testData/intentions/convertToRun/methodChain.kt.after create mode 100644 idea/testData/intentions/convertToRun/methodChainWithItParameter.kt create mode 100644 idea/testData/intentions/convertToRun/methodChainWithThisParameter.kt create mode 100644 idea/testData/intentions/convertToRun/normal.kt create mode 100644 idea/testData/intentions/convertToRun/normal.kt.after create mode 100644 idea/testData/intentions/convertToRun/normal2.kt create mode 100644 idea/testData/intentions/convertToRun/normal2.kt.after create mode 100644 idea/testData/intentions/convertToRun/normal3.kt create mode 100644 idea/testData/intentions/convertToRun/normal3.kt.after create mode 100644 idea/testData/intentions/convertToRun/thisParameter.kt create mode 100644 idea/testData/intentions/convertToRun/thisReceiver.kt create mode 100644 idea/testData/intentions/convertToRun/untilThisParameter.kt create mode 100644 idea/testData/intentions/convertToRun/untilThisParameter.kt.after create mode 100644 idea/testData/intentions/convertToRun/withCommentAndSpaces.kt create mode 100644 idea/testData/intentions/convertToRun/withCommentAndSpaces.kt.after create mode 100644 idea/testData/intentions/convertToWith/.intention create mode 100644 idea/testData/intentions/convertToWith/itReceiver.kt create mode 100644 idea/testData/intentions/convertToWith/methodChain.kt create mode 100644 idea/testData/intentions/convertToWith/methodChain.kt.after create mode 100644 idea/testData/intentions/convertToWith/methodChainWithItParameter.kt create mode 100644 idea/testData/intentions/convertToWith/methodChainWithThisParameter.kt create mode 100644 idea/testData/intentions/convertToWith/normal.kt create mode 100644 idea/testData/intentions/convertToWith/normal.kt.after create mode 100644 idea/testData/intentions/convertToWith/normal2.kt create mode 100644 idea/testData/intentions/convertToWith/normal2.kt.after create mode 100644 idea/testData/intentions/convertToWith/normal3.kt create mode 100644 idea/testData/intentions/convertToWith/normal3.kt.after create mode 100644 idea/testData/intentions/convertToWith/thisParameter.kt create mode 100644 idea/testData/intentions/convertToWith/thisReceiver.kt create mode 100644 idea/testData/intentions/convertToWith/untilThisParameter.kt create mode 100644 idea/testData/intentions/convertToWith/untilThisParameter.kt.after create mode 100644 idea/testData/intentions/convertToWith/withCommentAndSpaces.kt create mode 100644 idea/testData/intentions/convertToWith/withCommentAndSpaces.kt.after diff --git a/idea/resources/intentionDescriptions/ConvertToApplyIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertToApplyIntention/after.kt.template new file mode 100644 index 00000000000..7908c98ee97 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToApplyIntention/after.kt.template @@ -0,0 +1,7 @@ +fun foo() { + val a = MyClass().apply { + a.setFoo(1) + a.setBar(2) + a.setBaz(3) + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToApplyIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertToApplyIntention/before.kt.template new file mode 100644 index 00000000000..ba64950471d --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToApplyIntention/before.kt.template @@ -0,0 +1,6 @@ +fun foo() { + val a = MyClass() + a.setFoo(1) + a.setBar(2) + a.setBaz(3) +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToApplyIntention/description.html b/idea/resources/intentionDescriptions/ConvertToApplyIntention/description.html new file mode 100644 index 00000000000..b3f68f40255 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToApplyIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts several calls with same receiver to 'apply' + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToRunIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertToRunIntention/after.kt.template new file mode 100644 index 00000000000..2773236e76f --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToRunIntention/after.kt.template @@ -0,0 +1,7 @@ +fun foo() { + a.run { + setFoo(1) + setBar(2) + setBaz(3) + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToRunIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertToRunIntention/before.kt.template new file mode 100644 index 00000000000..6549b8c6d98 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToRunIntention/before.kt.template @@ -0,0 +1,5 @@ +fun foo() { + a.setFoo(1) + a.setBar(2) + a.setBaz(3) +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToRunIntention/description.html b/idea/resources/intentionDescriptions/ConvertToRunIntention/description.html new file mode 100644 index 00000000000..a46464bec9e --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToRunIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts several calls with same receiver to 'run' + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToWithIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertToWithIntention/after.kt.template new file mode 100644 index 00000000000..62b08b5dd0b --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToWithIntention/after.kt.template @@ -0,0 +1,7 @@ +fun foo() { + with(a) { + setFoo(1) + setBar(2) + setBaz(3) + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToWithIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertToWithIntention/before.kt.template new file mode 100644 index 00000000000..6549b8c6d98 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToWithIntention/before.kt.template @@ -0,0 +1,5 @@ +fun foo() { + a.setFoo(1) + a.setBar(2) + a.setBaz(3) +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertToWithIntention/description.html b/idea/resources/intentionDescriptions/ConvertToWithIntention/description.html new file mode 100644 index 00000000000..b8ef85d9ca9 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertToWithIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts several calls with same receiver to 'with' + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index ee733fb4bef..ef9536a42cf 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1512,6 +1512,21 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertToApplyIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToWithIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertToRunIntention + Kotlin + + (KtDotQualifiedExpression::class.java, text) { + + override fun isApplicableTo(element: KtDotQualifiedExpression, caretOffset: Int): Boolean { + val receiverExpressionText = element.getReceiverExpressionText() ?: return false + if (receiverExpressionText in BLACKLIST_RECEIVER_NAME) return false + if (!isApplicableWithGivenReceiverText(element, receiverExpressionText)) return false + val nextSibling = element.getDotQualifiedSiblingIfAny(forward = true) + if (nextSibling != null && isApplicableWithGivenReceiverText(nextSibling, receiverExpressionText)) return true + val prevSibling = element.getDotQualifiedSiblingIfAny(forward = false) + return prevSibling != null && isApplicableWithGivenReceiverText(prevSibling, receiverExpressionText) + } + + override fun applyTo(element: KtDotQualifiedExpression, editor: Editor?) { + val receiverExpressionText = element.getReceiverExpressionText() ?: return + applyWithGivenReceiverText(element, receiverExpressionText) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToApplyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToApplyIntention.kt new file mode 100644 index 00000000000..4516058904b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToApplyIntention.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2017 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.intentions + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments + +class ConvertToApplyIntention : ConvertToScopeIntention( + KtExpression::class.java, "Convert to apply" +) { + override fun findCallExpressionFrom(scopeExpression: KtExpression) = + ((scopeExpression as? KtProperty)?.initializer as? KtQualifiedExpression)?.callExpression + + override fun isApplicableTo(element: KtExpression, caretOffset: Int): Boolean { + return when (element) { + is KtProperty -> element.isApplicable() + is KtDotQualifiedExpression -> { + val receiverExpressionText = element.getLeftMostReceiverExpression().text + isApplicableWithGivenReceiverText(element, receiverExpressionText) && + element.findTargetProperty(receiverExpressionText)?.isApplicable() ?: false + } + else -> false + } + } + + private fun KtProperty.isApplicable(): Boolean { + if (!isLocal) return false + val nextSibling = getDotQualifiedSiblingIfAny(forward = true) + val localVariableName = name ?: return false + return nextSibling != null && isApplicableWithGivenReceiverText(nextSibling, localVariableName) + } + + private fun KtDotQualifiedExpression.findTargetProperty(receiverExpressionText: String): KtProperty? { + val target = getPrevSiblingIgnoringWhitespaceAndComments(false) + when (target) { + is KtProperty -> + if (target.name == receiverExpressionText) { + return target + } + is KtDotQualifiedExpression -> + if (isApplicableWithGivenReceiverText(target, receiverExpressionText)) { + return target.findTargetProperty(receiverExpressionText) + } + } + return null + } + + override fun applyTo(element: KtExpression, editor: Editor?) { + when (element) { + is KtProperty -> applyWithGivenReceiverText(element, element.name ?: return) + is KtDotQualifiedExpression -> { + val receiverExpressionText = element.getReceiverExpressionText() ?: return + val property = element.findTargetProperty(receiverExpressionText) ?: return + applyWithGivenReceiverText(property, receiverExpressionText) + } + } + } + + override fun createScopeExpression(factory: KtPsiFactory, element: KtExpression): KtProperty? { + if (element !is KtProperty) return null + val receiverExpressionText = element.name ?: return null + return factory.createProperty(receiverExpressionText, element.typeReference?.text, element.isVar, + "${element.initializer?.text}.apply{}") + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToRunIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToRunIntention.kt new file mode 100644 index 00000000000..934b508a6a9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToRunIntention.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2017 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.intentions + +import org.jetbrains.kotlin.psi.* + +class ConvertToRunIntention : ConvertDotQualifiedToScopeIntention("Convert to run") { + + override fun createScopeExpression(factory: KtPsiFactory, element: KtDotQualifiedExpression) = + factory.createExpressionByPattern("$0.run {}", element.getLeftMostReceiverExpression()) + + override fun findCallExpressionFrom(scopeExpression: KtExpression) = + (scopeExpression as? KtQualifiedExpression)?.callExpression +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToScopeIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToScopeIntention.kt new file mode 100644 index 00000000000..cb0c05914d2 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToScopeIntention.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2017 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.intentions + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments + +abstract class ConvertToScopeIntention( + elementType: Class, + text: String +) : SelfTargetingIntention(elementType, text) { + + protected val BLACKLIST_RECEIVER_NAME = listOf("this", "it") + + protected abstract fun createScopeExpression(factory: KtPsiFactory, element: TExpression): KtExpression? + + protected abstract fun findCallExpressionFrom(scopeExpression: KtExpression): KtCallExpression? + + protected fun KtDotQualifiedExpression.getReceiverExpressionText(): String? = getLeftMostReceiverExpression().text + + protected fun isApplicableWithGivenReceiverText(expression: KtDotQualifiedExpression, receiverExpressionText: String): Boolean { + if (receiverExpressionText != expression.getReceiverExpressionText()) return false + val callExpression = expression.callExpression ?: return false + if (!callExpression.isApplicable()) return false + val receiverExpression = expression.receiverExpression + return receiverExpression !is KtDotQualifiedExpression || + isApplicableWithGivenReceiverText(receiverExpression, receiverExpressionText) + } + + protected fun applyWithGivenReceiverText(expression: TExpression, receiverExpressionText: String) { + val factory = KtPsiFactory(expression) + val scopeBlockExpression = createScopeExpression(factory, expression) ?: return + val callExpression = findCallExpressionFrom(scopeBlockExpression) ?: return + val blockExpression = callExpression.getFirstLambdaArgumentBody() ?: return + val parent = expression.parent + val lastExpressionToMove = findLastExpressionToMove(receiverExpressionText, expression) + val firstTargetExpression = + if (expression is KtProperty) expression + else findFirstExpressionToMove(receiverExpressionText, expression) + val firstExpressionToMove = if (expression is KtProperty) expression.nextSibling else firstTargetExpression + blockExpression.moveRangeInto(firstExpressionToMove, lastExpressionToMove) + + parent.addBefore(scopeBlockExpression, firstTargetExpression) + parent.deleteChildRange(firstTargetExpression, lastExpressionToMove) + } + + protected fun KtExpression.getDotQualifiedSiblingIfAny(forward: Boolean): KtDotQualifiedExpression? { + val sibling = + if (forward) getNextSiblingIgnoringWhitespaceAndComments(false) + else getPrevSiblingIgnoringWhitespaceAndComments(false) + return sibling as? KtDotQualifiedExpression + } + + private fun KtCallExpression.getFirstLambdaArgumentBody() = + lambdaArguments.firstOrNull()?.getLambdaExpression()?.bodyExpression + + private fun KtBlockExpression.moveRangeInto( + firstElement: PsiElement, lastElement: PsiElement + ) { + addRange(firstElement, lastElement) + children.filterIsInstance(KtDotQualifiedExpression::class.java) + .forEach { it.deleteFirstReceiver() } + } + + private fun KtCallExpression.isApplicable() = lambdaArguments.isEmpty() && valueArguments.all { it.text !in BLACKLIST_RECEIVER_NAME } + + private fun findFirstExpressionToMove(receiverExpressionText: String, expression: KtExpression) = + findBoundaryExpression(receiverExpressionText, expression, forward = false) + + private fun findLastExpressionToMove(receiverExpressionText: String, expression: KtExpression) = + findBoundaryExpression(receiverExpressionText, expression, forward = true) + + private fun findBoundaryExpression(receiverExpressionText: String, expression: KtExpression, forward: Boolean): KtExpression { + var targetExpression: KtExpression = expression + while (true) { + val dotQualifiedSibling = targetExpression.getDotQualifiedSiblingIfAny(forward) + if (dotQualifiedSibling == null || !isApplicableWithGivenReceiverText(dotQualifiedSibling, receiverExpressionText)) { + return targetExpression + } + targetExpression = dotQualifiedSibling + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToWithIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToWithIntention.kt new file mode 100644 index 00000000000..c287f3ea0d8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToWithIntention.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2017 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.intentions + +import org.jetbrains.kotlin.psi.* + +class ConvertToWithIntention : ConvertDotQualifiedToScopeIntention("Convert to with") { + + override fun createScopeExpression(factory: KtPsiFactory, element: KtDotQualifiedExpression) = + factory.createExpressionByPattern("with($0) {}", element.getLeftMostReceiverExpression()) + + override fun findCallExpressionFrom(scopeExpression: KtExpression) = scopeExpression as? KtCallExpression +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/.intention b/idea/testData/intentions/convertToApply/.intention new file mode 100644 index 00000000000..f75db9df661 --- /dev/null +++ b/idea/testData/intentions/convertToApply/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertToApplyIntention diff --git a/idea/testData/intentions/convertToApply/methodChain.kt b/idea/testData/intentions/convertToApply/methodChain.kt new file mode 100644 index 00000000000..6f7c727bff6 --- /dev/null +++ b/idea/testData/intentions/convertToApply/methodChain.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4() { + val a = MyClass() + a.foo1().foo2().foo3() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/methodChain.kt.after b/idea/testData/intentions/convertToApply/methodChain.kt.after new file mode 100644 index 00000000000..59d3b83a1a5 --- /dev/null +++ b/idea/testData/intentions/convertToApply/methodChain.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4() { + val a = MyClass().apply { + foo1().foo2().foo3() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/methodChainWithItParameter.kt b/idea/testData/intentions/convertToApply/methodChainWithItParameter.kt new file mode 100644 index 00000000000..794112733ec --- /dev/null +++ b/idea/testData/intentions/convertToApply/methodChainWithItParameter.kt @@ -0,0 +1,17 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4() { + listOf().forEach { + val a = MyClass() + a.foo1(it).foo2().foo3() + a.foo2() + a.foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/methodChainWithThisParameter.kt b/idea/testData/intentions/convertToApply/methodChainWithThisParameter.kt new file mode 100644 index 00000000000..60f1b3f8a15 --- /dev/null +++ b/idea/testData/intentions/convertToApply/methodChainWithThisParameter.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4() { + val a = MyClass() + a.foo1(this).foo2().foo3() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal.kt b/idea/testData/intentions/convertToApply/normal.kt new file mode 100644 index 00000000000..3861e44ece4 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4() { + val a = MyClass() + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal.kt.after b/idea/testData/intentions/convertToApply/normal.kt.after new file mode 100644 index 00000000000..4a692907b17 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4() { + val a = MyClass().apply { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal2.kt b/idea/testData/intentions/convertToApply/normal2.kt new file mode 100644 index 00000000000..fc916d20946 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal2.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass() + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal2.kt.after b/idea/testData/intentions/convertToApply/normal2.kt.after new file mode 100644 index 00000000000..771cf853654 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal2.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass().apply { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal3.kt b/idea/testData/intentions/convertToApply/normal3.kt new file mode 100644 index 00000000000..7e99d66ecc9 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal3.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass() + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/normal3.kt.after b/idea/testData/intentions/convertToApply/normal3.kt.after new file mode 100644 index 00000000000..771cf853654 --- /dev/null +++ b/idea/testData/intentions/convertToApply/normal3.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass().apply { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/thisParameter.kt b/idea/testData/intentions/convertToApply/thisParameter.kt new file mode 100644 index 00000000000..aae9f6fd0f9 --- /dev/null +++ b/idea/testData/intentions/convertToApply/thisParameter.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass() + a.foo1() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/thisParameter2.kt b/idea/testData/intentions/convertToApply/thisParameter2.kt new file mode 100644 index 00000000000..5b00a990995 --- /dev/null +++ b/idea/testData/intentions/convertToApply/thisParameter2.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass() + a.foo1() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/untilThisParameter.kt b/idea/testData/intentions/convertToApply/untilThisParameter.kt new file mode 100644 index 00000000000..ae6721d3af1 --- /dev/null +++ b/idea/testData/intentions/convertToApply/untilThisParameter.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass() + a.foo1() + a.foo3() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/untilThisParameter.kt.after b/idea/testData/intentions/convertToApply/untilThisParameter.kt.after new file mode 100644 index 00000000000..df3031a6860 --- /dev/null +++ b/idea/testData/intentions/convertToApply/untilThisParameter.kt.after @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + val a = MyClass().apply { + foo1() + foo3() + } + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/var.kt b/idea/testData/intentions/convertToApply/var.kt new file mode 100644 index 00000000000..834f65165bc --- /dev/null +++ b/idea/testData/intentions/convertToApply/var.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4() { + var a = MyClass() + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/var.kt.after b/idea/testData/intentions/convertToApply/var.kt.after new file mode 100644 index 00000000000..fc61e52ec6c --- /dev/null +++ b/idea/testData/intentions/convertToApply/var.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4() { + var a = MyClass().apply { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt b/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt new file mode 100644 index 00000000000..9ab6a8cef87 --- /dev/null +++ b/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt @@ -0,0 +1,20 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + val a = MyClass() + // here is comment + a.foo1() + // comment + + // bbb + a.foo2() + a.foo3() + // last comment won't be in + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt.after b/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt.after new file mode 100644 index 00000000000..903eca8a00a --- /dev/null +++ b/idea/testData/intentions/convertToApply/withCommentAndSpaces.kt.after @@ -0,0 +1,21 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + val a = MyClass().apply { + // here is comment + foo1() + // comment + + // bbb + foo2() + foo3() + } + // last comment won't be in + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/.intention b/idea/testData/intentions/convertToRun/.intention new file mode 100644 index 00000000000..bc70a083d5c --- /dev/null +++ b/idea/testData/intentions/convertToRun/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertToRunIntention diff --git a/idea/testData/intentions/convertToRun/itReceiver.kt b/idea/testData/intentions/convertToRun/itReceiver.kt new file mode 100644 index 00000000000..753d242c788 --- /dev/null +++ b/idea/testData/intentions/convertToRun/itReceiver.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.let { + it.foo1() + it.foo2() + it.foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/methodChain.kt b/idea/testData/intentions/convertToRun/methodChain.kt new file mode 100644 index 00000000000..c23ecfbbaa7 --- /dev/null +++ b/idea/testData/intentions/convertToRun/methodChain.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + a.foo1().foo2().foo3() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/methodChain.kt.after b/idea/testData/intentions/convertToRun/methodChain.kt.after new file mode 100644 index 00000000000..71aee818f12 --- /dev/null +++ b/idea/testData/intentions/convertToRun/methodChain.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + a.run { + foo1().foo2().foo3() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/methodChainWithItParameter.kt b/idea/testData/intentions/convertToRun/methodChainWithItParameter.kt new file mode 100644 index 00000000000..4d41f35623b --- /dev/null +++ b/idea/testData/intentions/convertToRun/methodChainWithItParameter.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + listOf().forEach { + a.foo1(it).foo2().foo3() + a.foo2() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/methodChainWithThisParameter.kt b/idea/testData/intentions/convertToRun/methodChainWithThisParameter.kt new file mode 100644 index 00000000000..d32e1286c1c --- /dev/null +++ b/idea/testData/intentions/convertToRun/methodChainWithThisParameter.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + a.foo1(this).foo2().foo3() + a.foo2() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal.kt b/idea/testData/intentions/convertToRun/normal.kt new file mode 100644 index 00000000000..9eb577c1a93 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal.kt.after b/idea/testData/intentions/convertToRun/normal.kt.after new file mode 100644 index 00000000000..c5ad68fde03 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.run { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal2.kt b/idea/testData/intentions/convertToRun/normal2.kt new file mode 100644 index 00000000000..9ea78216bd0 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal2.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal2.kt.after b/idea/testData/intentions/convertToRun/normal2.kt.after new file mode 100644 index 00000000000..c5ad68fde03 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal2.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.run { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal3.kt b/idea/testData/intentions/convertToRun/normal3.kt new file mode 100644 index 00000000000..ac2db067b00 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal3.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/normal3.kt.after b/idea/testData/intentions/convertToRun/normal3.kt.after new file mode 100644 index 00000000000..c5ad68fde03 --- /dev/null +++ b/idea/testData/intentions/convertToRun/normal3.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.run { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/thisParameter.kt b/idea/testData/intentions/convertToRun/thisParameter.kt new file mode 100644 index 00000000000..95d112cc5de --- /dev/null +++ b/idea/testData/intentions/convertToRun/thisParameter.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/thisReceiver.kt b/idea/testData/intentions/convertToRun/thisReceiver.kt new file mode 100644 index 00000000000..14770858e8f --- /dev/null +++ b/idea/testData/intentions/convertToRun/thisReceiver.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + this.foo1() + this.foo2() + this.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/untilThisParameter.kt b/idea/testData/intentions/convertToRun/untilThisParameter.kt new file mode 100644 index 00000000000..215e4f6bf60 --- /dev/null +++ b/idea/testData/intentions/convertToRun/untilThisParameter.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo3() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/untilThisParameter.kt.after b/idea/testData/intentions/convertToRun/untilThisParameter.kt.after new file mode 100644 index 00000000000..1fb0903f3b5 --- /dev/null +++ b/idea/testData/intentions/convertToRun/untilThisParameter.kt.after @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.run { + foo1() + foo3() + } + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt b/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt new file mode 100644 index 00000000000..208fed9039d --- /dev/null +++ b/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + a.foo1() + // comment + + // bbb + a.foo2() + a.foo3() + // last comment + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt.after b/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt.after new file mode 100644 index 00000000000..e04d0d1f2d2 --- /dev/null +++ b/idea/testData/intentions/convertToRun/withCommentAndSpaces.kt.after @@ -0,0 +1,20 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + a.run { + foo1() + // comment + + // bbb + foo2() + foo3() + } + // last comment + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/.intention b/idea/testData/intentions/convertToWith/.intention new file mode 100644 index 00000000000..1f4f2f8021d --- /dev/null +++ b/idea/testData/intentions/convertToWith/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertToWithIntention diff --git a/idea/testData/intentions/convertToWith/itReceiver.kt b/idea/testData/intentions/convertToWith/itReceiver.kt new file mode 100644 index 00000000000..753d242c788 --- /dev/null +++ b/idea/testData/intentions/convertToWith/itReceiver.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.let { + it.foo1() + it.foo2() + it.foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/methodChain.kt b/idea/testData/intentions/convertToWith/methodChain.kt new file mode 100644 index 00000000000..c23ecfbbaa7 --- /dev/null +++ b/idea/testData/intentions/convertToWith/methodChain.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + a.foo1().foo2().foo3() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/methodChain.kt.after b/idea/testData/intentions/convertToWith/methodChain.kt.after new file mode 100644 index 00000000000..ddd5d23c4b5 --- /dev/null +++ b/idea/testData/intentions/convertToWith/methodChain.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1(): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + with(a) { + foo1().foo2().foo3() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/methodChainWithItParameter.kt b/idea/testData/intentions/convertToWith/methodChainWithItParameter.kt new file mode 100644 index 00000000000..4d41f35623b --- /dev/null +++ b/idea/testData/intentions/convertToWith/methodChainWithItParameter.kt @@ -0,0 +1,15 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + listOf().forEach { + a.foo1(it).foo2().foo3() + a.foo2() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/methodChainWithThisParameter.kt b/idea/testData/intentions/convertToWith/methodChainWithThisParameter.kt new file mode 100644 index 00000000000..d32e1286c1c --- /dev/null +++ b/idea/testData/intentions/convertToWith/methodChainWithThisParameter.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1(a: MyClass): MyClass = this + fun foo2(): MyClass = this + fun foo3(): MyClass = this + + fun foo4(a: MyClass) { + a.foo1(this).foo2().foo3() + a.foo2() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal.kt b/idea/testData/intentions/convertToWith/normal.kt new file mode 100644 index 00000000000..9eb577c1a93 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal.kt.after b/idea/testData/intentions/convertToWith/normal.kt.after new file mode 100644 index 00000000000..8a84060ff46 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + with(a) { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal2.kt b/idea/testData/intentions/convertToWith/normal2.kt new file mode 100644 index 00000000000..9ea78216bd0 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal2.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal2.kt.after b/idea/testData/intentions/convertToWith/normal2.kt.after new file mode 100644 index 00000000000..8a84060ff46 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal2.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + with(a) { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal3.kt b/idea/testData/intentions/convertToWith/normal3.kt new file mode 100644 index 00000000000..ac2db067b00 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal3.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2() + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/normal3.kt.after b/idea/testData/intentions/convertToWith/normal3.kt.after new file mode 100644 index 00000000000..8a84060ff46 --- /dev/null +++ b/idea/testData/intentions/convertToWith/normal3.kt.after @@ -0,0 +1,15 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + with(a) { + foo1() + foo2() + foo3() + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/thisParameter.kt b/idea/testData/intentions/convertToWith/thisParameter.kt new file mode 100644 index 00000000000..95d112cc5de --- /dev/null +++ b/idea/testData/intentions/convertToWith/thisParameter.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/thisReceiver.kt b/idea/testData/intentions/convertToWith/thisReceiver.kt new file mode 100644 index 00000000000..14770858e8f --- /dev/null +++ b/idea/testData/intentions/convertToWith/thisReceiver.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + this.foo1() + this.foo2() + this.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/untilThisParameter.kt b/idea/testData/intentions/convertToWith/untilThisParameter.kt new file mode 100644 index 00000000000..215e4f6bf60 --- /dev/null +++ b/idea/testData/intentions/convertToWith/untilThisParameter.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + a.foo1() + a.foo3() + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/untilThisParameter.kt.after b/idea/testData/intentions/convertToWith/untilThisParameter.kt.after new file mode 100644 index 00000000000..aea8f024a8e --- /dev/null +++ b/idea/testData/intentions/convertToWith/untilThisParameter.kt.after @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2(a: MyClass) = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + with(a) { + foo1() + foo3() + } + a.foo2(this) + a.foo3() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt b/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt new file mode 100644 index 00000000000..208fed9039d --- /dev/null +++ b/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + a.foo1() + // comment + + // bbb + a.foo2() + a.foo3() + // last comment + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt.after b/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt.after new file mode 100644 index 00000000000..90da9bd3b8f --- /dev/null +++ b/idea/testData/intentions/convertToWith/withCommentAndSpaces.kt.after @@ -0,0 +1,20 @@ +// WITH_RUNTIME + +class MyClass { + fun foo1() = Unit + fun foo2() = Unit + fun foo3() = Unit + + fun foo4(a: MyClass) { + // top comment + with(a) { + foo1() + // comment + + // bbb + foo2() + foo3() + } + // last comment + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 6f56077b1ae..149322dfd7a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -5637,6 +5637,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertToApply") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertToApply extends AbstractIntentionTest { + public void testAllFilesPresentInConvertToApply() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToApply"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("methodChain.kt") + public void testMethodChain() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/methodChain.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithItParameter.kt") + public void testMethodChainWithItParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/methodChainWithItParameter.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithThisParameter.kt") + public void testMethodChainWithThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/methodChainWithThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/normal.kt"); + doTest(fileName); + } + + @TestMetadata("normal2.kt") + public void testNormal2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/normal2.kt"); + doTest(fileName); + } + + @TestMetadata("normal3.kt") + public void testNormal3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/normal3.kt"); + doTest(fileName); + } + + @TestMetadata("thisParameter.kt") + public void testThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/thisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("thisParameter2.kt") + public void testThisParameter2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/thisParameter2.kt"); + doTest(fileName); + } + + @TestMetadata("untilThisParameter.kt") + public void testUntilThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/untilThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("var.kt") + public void testVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/var.kt"); + doTest(fileName); + } + + @TestMetadata("withCommentAndSpaces.kt") + public void testWithCommentAndSpaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToApply/withCommentAndSpaces.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertToBlockBody") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -6318,6 +6393,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertToRun") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertToRun extends AbstractIntentionTest { + public void testAllFilesPresentInConvertToRun() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToRun"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("itReceiver.kt") + public void testItReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/itReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("methodChain.kt") + public void testMethodChain() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/methodChain.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithItParameter.kt") + public void testMethodChainWithItParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/methodChainWithItParameter.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithThisParameter.kt") + public void testMethodChainWithThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/methodChainWithThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/normal.kt"); + doTest(fileName); + } + + @TestMetadata("normal2.kt") + public void testNormal2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/normal2.kt"); + doTest(fileName); + } + + @TestMetadata("normal3.kt") + public void testNormal3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/normal3.kt"); + doTest(fileName); + } + + @TestMetadata("thisParameter.kt") + public void testThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/thisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("thisReceiver.kt") + public void testThisReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/thisReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("untilThisParameter.kt") + public void testUntilThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/untilThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("withCommentAndSpaces.kt") + public void testWithCommentAndSpaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToRun/withCommentAndSpaces.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertToStringTemplate") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -6556,6 +6706,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } + @TestMetadata("idea/testData/intentions/convertToWith") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertToWith extends AbstractIntentionTest { + public void testAllFilesPresentInConvertToWith() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToWith"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("itReceiver.kt") + public void testItReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/itReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("methodChain.kt") + public void testMethodChain() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/methodChain.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithItParameter.kt") + public void testMethodChainWithItParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/methodChainWithItParameter.kt"); + doTest(fileName); + } + + @TestMetadata("methodChainWithThisParameter.kt") + public void testMethodChainWithThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/methodChainWithThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/normal.kt"); + doTest(fileName); + } + + @TestMetadata("normal2.kt") + public void testNormal2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/normal2.kt"); + doTest(fileName); + } + + @TestMetadata("normal3.kt") + public void testNormal3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/normal3.kt"); + doTest(fileName); + } + + @TestMetadata("thisParameter.kt") + public void testThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/thisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("thisReceiver.kt") + public void testThisReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/thisReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("untilThisParameter.kt") + public void testUntilThisParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/untilThisParameter.kt"); + doTest(fileName); + } + + @TestMetadata("withCommentAndSpaces.kt") + public void testWithCommentAndSpaces() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertToWith/withCommentAndSpaces.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertTryFinallyToUseCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)