diff --git a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/UsageReplacementStrategy.kt b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/UsageReplacementStrategy.kt index a8e04f71fcb..d429299f9f9 100644 --- a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/UsageReplacementStrategy.kt +++ b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/UsageReplacementStrategy.kt @@ -44,7 +44,8 @@ private val LOG = Logger.getInstance(UsageReplacementStrategy::class.java) fun UsageReplacementStrategy.replaceUsagesInWholeProject( targetPsiElement: PsiElement, progressTitle: String, - commandName: String + commandName: String, + postAction: () -> Unit = {} ) { val project = targetPsiElement.project ProgressManager.getInstance().run( @@ -56,7 +57,7 @@ fun UsageReplacementStrategy.replaceUsagesInWholeProject( .filterIsInstance() .map { ref -> ref.expression } } - this@replaceUsagesInWholeProject.replaceUsages(usages, project, commandName) + this@replaceUsagesInWholeProject.replaceUsages(usages, project, commandName, postAction) } }) } @@ -64,7 +65,8 @@ fun UsageReplacementStrategy.replaceUsagesInWholeProject( private fun UsageReplacementStrategy.replaceUsages( usages: Collection, project: Project, - commandName: String + commandName: String, + postAction: () -> Unit ) { UIUtil.invokeLaterIfNeeded { project.executeWriteCommand(commandName) { @@ -92,6 +94,8 @@ private fun UsageReplacementStrategy.replaceUsages( } importsToDelete.forEach { it.delete() } + + postAction() } } } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 8db61849953..a40708e9be5 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -372,6 +372,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt new file mode 100644 index 00000000000..bee98ef69e9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineFunctionHandler.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2015 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.refactoring.inline + +import com.intellij.lang.Language +import com.intellij.lang.refactoring.InlineActionHandler +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.refactoring.RefactoringBundle +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.replacement.CallableUsageReplacementStrategy +import org.jetbrains.kotlin.idea.replacement.ReplacementBuilder +import org.jetbrains.kotlin.idea.replacement.replaceUsagesInWholeProject +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.psi.KtNamedFunction + +class KotlinInlineFunctionHandler: InlineActionHandler() { + override fun isEnabledForLanguage(language: Language) = language == KotlinLanguage.INSTANCE + + override fun canInlineElement(element: PsiElement): Boolean { + return element is KtNamedFunction + && element.hasBody() && !element.hasBlockBody() // TODO support multiline functions + && element.getUseScope() is GlobalSearchScope // TODO support local functions + } + + override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) { + element as KtNamedFunction + + val descriptor = element.resolveToDescriptor() as SimpleFunctionDescriptor + + val bodyExpression = element.bodyExpression!! + val replacement = ReplacementBuilder(descriptor, element.getResolutionFacade()) + .buildReplacementExpression(bodyExpression, bodyExpression.getResolutionScope()) + + val commandName = RefactoringBundle.message("inline.command", element.name) + CallableUsageReplacementStrategy(replacement) + .replaceUsagesInWholeProject(element, commandName, commandName, postAction = { element.delete() }) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt new file mode 100644 index 00000000000..29b8c82cbb0 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt @@ -0,0 +1,11 @@ +fun f(p: Int) = g() + +fun g() { +} + +fun complexFun(): Int { +} + +fun main(args: Array) { + f(complexFun()) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt.after b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt.after new file mode 100644 index 00000000000..aef93bb58f0 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt.after @@ -0,0 +1,10 @@ +fun g() { +} + +fun complexFun(): Int { +} + +fun main(args: Array) { + complexFun() + g() +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt new file mode 100644 index 00000000000..1edcabc012b --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt @@ -0,0 +1,8 @@ +fun f(p: Int) = p + p + +fun complexFun(): Int { +} + +fun main(args: Array) { + f(complexFun()) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt.after b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt.after new file mode 100644 index 00000000000..0ee242ef906 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt.after @@ -0,0 +1,7 @@ +fun complexFun(): Int { +} + +fun main(args: Array) { + val p = complexFun() + p + p +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/Constant.kt b/idea/testData/refactoring/inline/function/expressionBody/Constant.kt new file mode 100644 index 00000000000..cff27310b5e --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/Constant.kt @@ -0,0 +1,5 @@ +fun f() = "foo" + +fun main(args: Array) { + println(f() + f()) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/Constant.kt.after b/idea/testData/refactoring/inline/function/expressionBody/Constant.kt.after new file mode 100644 index 00000000000..cddff1fe5c7 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/Constant.kt.after @@ -0,0 +1,3 @@ +fun main(args: Array) { + println("foo" + "foo") +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt b/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt new file mode 100644 index 00000000000..34ed3fc42f4 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt @@ -0,0 +1,7 @@ +fun f(p1: Int, p2: Int) = p1 + p2 + +fun main(args: Array) { + println(f(1, 2)) + println(f(3, 4)) + println(f(5, 6)) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt.after b/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt.after new file mode 100644 index 00000000000..c6861e47bb6 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt.after @@ -0,0 +1,5 @@ +fun main(args: Array) { + println(1 + 2) + println(3 + 4) + println(5 + 6) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt b/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt new file mode 100644 index 00000000000..c9d27fd17cd --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt @@ -0,0 +1,10 @@ +fun foo(p1: String, p2: () -> Boolean) = bar(p1, null, p2) + +fun bar(p1: String, p2: String?, p3: () -> Boolean) + +fun foo(i: I) { + foo("foo") { + println("bar") + println("baz") + } +} diff --git a/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt.after b/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt.after new file mode 100644 index 00000000000..ca809dec7f3 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt.after @@ -0,0 +1,8 @@ +fun bar(p1: String, p2: String?, p3: () -> Boolean) + +fun foo(i: I) { + bar("foo", null) { + println("bar") + println("baz") + } +} diff --git a/idea/testData/refactoring/inline/function/expressionBody/Simple.kt b/idea/testData/refactoring/inline/function/expressionBody/Simple.kt new file mode 100644 index 00000000000..f2b491db1f8 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/Simple.kt @@ -0,0 +1,5 @@ +fun f(p1: Int, p2: Int) = p1 + p2 + +fun main(args: Array) { + println(f(3, 5)) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/Simple.kt.after b/idea/testData/refactoring/inline/function/expressionBody/Simple.kt.after new file mode 100644 index 00000000000..e1e0a927c48 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/Simple.kt.after @@ -0,0 +1,3 @@ +fun main(args: Array) { + println(3 + 5) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index 832c9604391..670c455c7ee 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -35,6 +35,60 @@ public class InlineTestGenerated extends AbstractInlineTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline"), Pattern.compile("^(\\w+)\\.kt$"), true); } + @TestMetadata("idea/testData/refactoring/inline/function") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Function extends AbstractInlineTest { + public void testAllFilesPresentInFunction() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline/function"), Pattern.compile("^(\\w+)\\.kt$"), true); + } + + @TestMetadata("idea/testData/refactoring/inline/function/expressionBody") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ExpressionBody extends AbstractInlineTest { + public void testAllFilesPresentInExpressionBody() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline/function/expressionBody"), Pattern.compile("^(\\w+)\\.kt$"), true); + } + + @TestMetadata("ComplexArgumentNotUsed.kt") + public void testComplexArgumentNotUsed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentNotUsed.kt"); + doTest(fileName); + } + + @TestMetadata("ComplexArgumentUsedTwice.kt") + public void testComplexArgumentUsedTwice() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/ComplexArgumentUsedTwice.kt"); + doTest(fileName); + } + + @TestMetadata("Constant.kt") + public void testConstant() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/Constant.kt"); + doTest(fileName); + } + + @TestMetadata("FromUsage.kt") + public void testFromUsage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/FromUsage.kt"); + doTest(fileName); + } + + @TestMetadata("FunctionalParameterPassed.kt") + public void testFunctionalParameterPassed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/FunctionalParameterPassed.kt"); + doTest(fileName); + } + + @TestMetadata("Simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/Simple.kt"); + doTest(fileName); + } + } + } + @TestMetadata("idea/testData/refactoring/inline/inlineTypeAlias") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)