diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index 3d8a46fa2f1..c374db2e57a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -566,10 +566,14 @@ public class JetPsiFactory(private val project: Project) { } } - public fun createFunctionBody(bodyText: String): JetBlockExpression { + public fun createBlock(bodyText: String): JetBlockExpression { return createFunction("fun foo() {\n" + bodyText + "\n}").getBodyExpression() as JetBlockExpression } + public fun createSingleStatementBlock(statement: JetExpression): JetBlockExpression { + return createDeclarationByPattern("fun foo() {\n$0\n}", statement).getBodyExpression() as JetBlockExpression + } + public fun createComment(text: String): PsiComment { val file = createFile(text) val comments = file.getChildren().filterIsInstance() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 6f672d594d5..4120e13fc92 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -573,12 +573,16 @@ public inline fun PsiElement.forEachDescendantOfType(no } public inline fun PsiElement.anyDescendantOfType(noinline predicate: (T) -> Boolean): Boolean { - var result = false + return findDescendantOfType(predicate) != null +} + +public inline fun PsiElement.findDescendantOfType(noinline predicate: (T) -> Boolean): T? { + var result: T? = null this.accept(object : PsiRecursiveElementVisitor(){ override fun visitElement(element: PsiElement) { - if (result) return + if (result != null) return if (element is T && predicate(element)) { - result = true + result = element return } super.visitElement(element) @@ -587,7 +591,7 @@ public inline fun PsiElement.anyDescendantOfType(noinli return result } -public inline fun PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): Collection { +public inline fun PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List { val result = ArrayList() forEachDescendantOfType { if (predicate(it)) { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddBracesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddBracesIntention.kt index d2dfb78e182..57892487ac3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AddBracesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddBracesIntention.kt @@ -39,7 +39,7 @@ public class AddBracesIntention : JetSelfTargetingIntention(javaC } val psiFactory = JetPsiFactory(element) - expression.replace(psiFactory.createFunctionBody(expression.getText())) + expression.replace(psiFactory.createSingleStatementBlock(expression)) if (element is JetDoWhileExpression) { // remove new line between '}' and while (element.getBody()!!.getParent().getNextSibling() as? PsiWhiteSpace)?.delete() diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt index 66c55418f5b..70b0c6e5d8e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt @@ -56,9 +56,9 @@ public class ConvertToBlockBodyIntention : JetSelfTargetingIntention { if (!extension.hasBody()) { - extension.add(psiFactory.createFunctionBody(THROW_UNSUPPORTED_OPERATION_EXCEPTION)) + extension.add(psiFactory.createBlock(THROW_UNSUPPORTED_OPERATION_EXCEPTION)) selectBody(extension) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index f22e724f08e..cf16e0c0a25 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -708,7 +708,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { throw IncorrectOperationException("Failed to parse file template", e) } - oldBody.replace(JetPsiFactory(func).createFunctionBody(bodyText)) + oldBody.replace(JetPsiFactory(func).createBlock(bodyText)) } private fun setupCallTypeArguments(callElement: JetCallElement, typeParameters: List) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt index 1a04d6f603f..ee823127791 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceUtil.kt @@ -16,14 +16,21 @@ package org.jetbrains.kotlin.idea.refactoring.introduce -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.idea.refactoring.* -import com.intellij.openapi.editor.* -import com.intellij.psi.* -import com.intellij.psi.util.* -import org.jetbrains.kotlin.idea.codeInsight.* -import com.intellij.openapi.project.* +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.ScrollType +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary +import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle +import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType +import org.jetbrains.kotlin.psi.psiUtil.getOutermostParentContainedIn fun showErrorHint(project: Project, editor: Editor, message: String, title: String) { CodeInsightUtils.showErrorHint(project, editor, message, title, null) @@ -121,4 +128,22 @@ fun selectElementsWithTargetParent( editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE) selectSingleExpression() -} \ No newline at end of file +} + +fun PsiElement.findExpressionByCopyableDataAndClearIt(key: Key): JetExpression { + val result = findDescendantOfType { it.getCopyableUserData(key) != null }!! + result.putCopyableUserData(key, null) + return result +} + +fun PsiElement.findElementByCopyableDataAndClearIt(key: Key): PsiElement { + val result = findDescendantOfType { it.getCopyableUserData(key) != null }!! + result.putCopyableUserData(key, null) + return result +} + +fun PsiElement.findExpressionsByCopyableDataAndClearIt(key: Key): List { + val results = collectDescendantsOfType { it.getCopyableUserData(key) != null } + results.forEach { it.putCopyableUserData(key, null) } + return results +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java index be57c16e82d..3fd4e940d59 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiDocumentManager; @@ -45,6 +46,7 @@ import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntenti import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl; import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle; import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil; +import org.jetbrains.kotlin.idea.refactoring.introduce.IntroducePackage; import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceHandlerBase; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.idea.util.ShortenReferences; @@ -65,10 +67,7 @@ import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.checker.JetTypeChecker; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.*; import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; @@ -272,28 +271,24 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase { final JetExpression originalBody = originalDeclaration.getBodyExpression(); assert originalBody != null : "Original body is not found: " + originalDeclaration; + Key EXPRESSION_KEY = Key.create("EXPRESSION_KEY"); + Key REPLACE_KEY = Key.create("REPLACE_KEY"); + Key COMMON_PARENT_KEY = Key.create("COMMON_PARENT_KEY"); + expression.putCopyableUserData(EXPRESSION_KEY, true); + for (JetExpression replace : allReplaces) { + replace.putCopyableUserData(REPLACE_KEY, true); + } + commonParent.putCopyableUserData(COMMON_PARENT_KEY, true); + JetDeclarationWithBody newDeclaration = ConvertToBlockBodyIntention.Companion.convert(originalDeclaration); JetBlockExpression newCommonContainer = (JetBlockExpression) newDeclaration.getBodyExpression(); assert newCommonContainer != null : "New body is not found: " + newDeclaration; - JetExpression resultExpression = (JetExpression) newCommonContainer.getStatements().get(0); - if (resultExpression instanceof JetReturnExpression && !(originalBody instanceof JetReturnExpression)) { - resultExpression = ((JetReturnExpression) resultExpression).getReturnedExpression(); - } - final JetExpression finalResultExpression = resultExpression; + JetExpression newExpression = IntroducePackage.findExpressionByCopyableDataAndClearIt(newCommonContainer, EXPRESSION_KEY); + PsiElement newCommonParent = IntroducePackage.findElementByCopyableDataAndClearIt(newCommonContainer, COMMON_PARENT_KEY); + List newAllReplaces = IntroducePackage.findExpressionsByCopyableDataAndClearIt(newCommonContainer, REPLACE_KEY); - JetExpression newExpression = (JetExpression) findElementCounterpart(expression, originalBody, resultExpression); - PsiElement newCommonParent = findElementCounterpart(commonParent, originalBody, resultExpression); - List newAllReplaces = KotlinPackage.map( - allReplaces, - new Function1() { - @Override - public JetExpression invoke(JetExpression expression) { - return (JetExpression) findElementCounterpart(expression, originalBody, finalResultExpression); - } - } - ); run(newExpression, newCommonContainer, newCommonParent, newAllReplaces); } else { @@ -438,14 +433,6 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase { } } - private PsiElement findElementCounterpart(PsiElement oldElement, PsiElement oldContainer, PsiElement newContainer) { - return findElementByOffsetAndText( - oldElement.getTextOffset() - oldContainer.getTextOffset(), - oldElement.getText(), - newContainer - ); - } - private PsiElement findElementByOffsetAndText(int offset, String text, PsiElement newContainer) { PsiElement elem = newContainer.findElementAt(offset); while (elem != null && !(elem instanceof JetExpression && text.equals(elem.getText()))) { diff --git a/idea/testData/intentions/convertToBlockBody/addSpace.kt b/idea/testData/intentions/convertToBlockBody/addSpace.kt new file mode 100644 index 00000000000..3f9b3d83bef --- /dev/null +++ b/idea/testData/intentions/convertToBlockBody/addSpace.kt @@ -0,0 +1,3 @@ +fun foo(): Int=bar() + +fun bar() = 1 \ No newline at end of file diff --git a/idea/testData/intentions/convertToBlockBody/addSpace.kt.after b/idea/testData/intentions/convertToBlockBody/addSpace.kt.after new file mode 100644 index 00000000000..88988a43324 --- /dev/null +++ b/idea/testData/intentions/convertToBlockBody/addSpace.kt.after @@ -0,0 +1,5 @@ +fun foo(): Int { + return bar() +} + +fun bar() = 1 \ 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 900ba3ba3c9..48d6797f628 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -3417,6 +3417,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class ConvertToBlockBody extends AbstractIntentionTest { + @TestMetadata("addSpace.kt") + public void testAddSpace() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/convertToBlockBody/addSpace.kt"); + doTest(fileName); + } + public void testAllFilesPresentInConvertToBlockBody() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertToBlockBody"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); }