From a9a960fd3881364fa558ec9a6d522c297a3f0884 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Fri, 5 Jul 2013 22:05:55 +0400 Subject: [PATCH] Basic local val inlining support. #KT-2637 in progress --- .../jet/generators/tests/GenerateTests.java | 8 ++ idea/src/META-INF/plugin.xml | 1 + .../inline/KotlinInlineLocalHandler.java | 94 +++++++++++++++++++ idea/testData/refactoring/inline/Basic.kt | 4 + .../refactoring/inline/Basic.kt.after | 3 + .../inline/BasicCaretOnDeclaration.kt | 4 + .../inline/BasicCaretOnDeclaration.kt.after | 3 + .../refactoring/inline/InFunctionLiteral.kt | 6 ++ .../inline/InFunctionLiteral.kt.after | 5 + .../refactoring/inline/MethodReference.kt | 4 + .../inline/MethodReference.kt.after | 3 + .../refactoring/inline/MultipleUsages.kt | 5 + .../inline/MultipleUsages.kt.after | 4 + idea/testData/refactoring/inline/Parameter.kt | 3 + .../inline/ValWithoutInitializer.kt | 4 + idea/testData/refactoring/inline/Var.kt | 4 + .../inline/AbstractInlineTest.java | 41 ++++++++ .../inline/InlineTestGenerated.java | 79 ++++++++++++++++ 18 files changed, 275 insertions(+) create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java create mode 100644 idea/testData/refactoring/inline/Basic.kt create mode 100644 idea/testData/refactoring/inline/Basic.kt.after create mode 100644 idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt create mode 100644 idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt.after create mode 100644 idea/testData/refactoring/inline/InFunctionLiteral.kt create mode 100644 idea/testData/refactoring/inline/InFunctionLiteral.kt.after create mode 100644 idea/testData/refactoring/inline/MethodReference.kt create mode 100644 idea/testData/refactoring/inline/MethodReference.kt.after create mode 100644 idea/testData/refactoring/inline/MultipleUsages.kt create mode 100644 idea/testData/refactoring/inline/MultipleUsages.kt.after create mode 100644 idea/testData/refactoring/inline/Parameter.kt create mode 100644 idea/testData/refactoring/inline/ValWithoutInitializer.kt create mode 100644 idea/testData/refactoring/inline/Var.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java create mode 100644 idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java diff --git a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java index f2ec4c9a962..9337aa1fc1f 100644 --- a/generators/org/jetbrains/jet/generators/tests/GenerateTests.java +++ b/generators/org/jetbrains/jet/generators/tests/GenerateTests.java @@ -47,6 +47,7 @@ import org.jetbrains.jet.plugin.intentions.AbstractCodeTransformationTest; import org.jetbrains.jet.plugin.navigation.JetAbstractGotoSuperTest; import org.jetbrains.jet.plugin.quickfix.AbstractQuickFixMultiFileTest; import org.jetbrains.jet.plugin.quickfix.AbstractQuickFixTest; +import org.jetbrains.jet.plugin.refactoring.inline.AbstractInlineTest; import org.jetbrains.jet.psi.AbstractJetPsiMatcherTest; import org.jetbrains.jet.resolve.AbstractResolveTest; import org.jetbrains.jet.safeDelete.AbstractJetSafeDeleteTest; @@ -373,6 +374,13 @@ public class GenerateTests { testModel("idea/testData/codeInsight/moveUpDown/expressions", "doTestExpression") ); + generateTest( + "idea/tests/", + "InlineTestGenerated", + AbstractInlineTest.class, + testModel("idea/testData/refactoring/inline", "doTest") + ); + generateTest( "idea/tests/", "UnwrapRemoveTestGenerated", diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index cd1122a7a2b..8a6b0fd0bfe 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -131,6 +131,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java new file mode 100644 index 00000000000..9a3a37e6bc0 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java @@ -0,0 +1,94 @@ +package org.jetbrains.jet.plugin.refactoring.inline; + +import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.lang.Language; +import com.intellij.lang.refactoring.InlineActionHandler; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.colors.EditorColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.util.RefactoringMessageDialog; +import com.intellij.util.Processor; +import com.intellij.util.Query; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.plugin.JetLanguage; + +import java.util.Collection; + +public class KotlinInlineLocalHandler extends InlineActionHandler { + @Override + public boolean isEnabledForLanguage(Language l) { + return l.equals(JetLanguage.INSTANCE); + } + + @Override + public boolean canInlineElement(PsiElement element) { + if (!(element instanceof JetProperty)) { + return false; + } + JetProperty property = (JetProperty) element; + return property.getInitializer() != null && !property.isVar(); + } + + @Override + public void inlineElement(Project project, Editor editor, PsiElement element) { + final JetProperty val = (JetProperty) element; + String name = val.getName(); + + final JetExpression initializer = val.getInitializer(); + assert initializer != null : element.getText(); + + final Collection references = ReferencesSearch.search(val, GlobalSearchScope.allScope(project), false).findAll(); + PsiReference[] referencesArray = references.toArray(references.toArray(new PsiReference[references.size()])); + + if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) { + EditorColorsManager editorColorsManager = EditorColorsManager.getInstance(); + TextAttributes attributes = editorColorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); + HighlightManager.getInstance(project).addOccurrenceHighlights(editor, referencesArray, attributes, true, null); + RefactoringMessageDialog dialog = new RefactoringMessageDialog( + RefactoringBundle.message("inline.variable.title"), + RefactoringBundle.message("inline.local.variable.prompt", name) + " " + + RefactoringBundle.message("occurences.string", references.size()), + HelpID.INLINE_VARIABLE, + "OptionPane.questionIcon", + true, + project); + + dialog.show(); + if (!dialog.isOK()){ + StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); + if (statusBar != null) { + statusBar.setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); + } + return; + } + } + + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + for (PsiReference reference : references) { + reference.getElement().replace(initializer.copy()); + } + + val.delete(); + } + }); + } + }, RefactoringBundle.message("inline.command", name), null); + } +} diff --git a/idea/testData/refactoring/inline/Basic.kt b/idea/testData/refactoring/inline/Basic.kt new file mode 100644 index 00000000000..d7e44ede812 --- /dev/null +++ b/idea/testData/refactoring/inline/Basic.kt @@ -0,0 +1,4 @@ +fun f() { + val v = 239 + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/Basic.kt.after b/idea/testData/refactoring/inline/Basic.kt.after new file mode 100644 index 00000000000..2dbc7327bef --- /dev/null +++ b/idea/testData/refactoring/inline/Basic.kt.after @@ -0,0 +1,3 @@ +fun f() { + println(239) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt b/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt new file mode 100644 index 00000000000..c6a7df779d8 --- /dev/null +++ b/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt @@ -0,0 +1,4 @@ +fun f() { + val v = 239 + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt.after b/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt.after new file mode 100644 index 00000000000..2dbc7327bef --- /dev/null +++ b/idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt.after @@ -0,0 +1,3 @@ +fun f() { + println(239) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/InFunctionLiteral.kt b/idea/testData/refactoring/inline/InFunctionLiteral.kt new file mode 100644 index 00000000000..973141e4e12 --- /dev/null +++ b/idea/testData/refactoring/inline/InFunctionLiteral.kt @@ -0,0 +1,6 @@ +fun f() { + run { + val v = 239 + println(v) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/InFunctionLiteral.kt.after b/idea/testData/refactoring/inline/InFunctionLiteral.kt.after new file mode 100644 index 00000000000..02064e0d60c --- /dev/null +++ b/idea/testData/refactoring/inline/InFunctionLiteral.kt.after @@ -0,0 +1,5 @@ +fun f() { + run { + println(239) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/MethodReference.kt b/idea/testData/refactoring/inline/MethodReference.kt new file mode 100644 index 00000000000..05ae232b75a --- /dev/null +++ b/idea/testData/refactoring/inline/MethodReference.kt @@ -0,0 +1,4 @@ +fun f() { + val m = ::println + println(m) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/MethodReference.kt.after b/idea/testData/refactoring/inline/MethodReference.kt.after new file mode 100644 index 00000000000..acb87710231 --- /dev/null +++ b/idea/testData/refactoring/inline/MethodReference.kt.after @@ -0,0 +1,3 @@ +fun f() { + println(::println) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/MultipleUsages.kt b/idea/testData/refactoring/inline/MultipleUsages.kt new file mode 100644 index 00000000000..07ae7c56e76 --- /dev/null +++ b/idea/testData/refactoring/inline/MultipleUsages.kt @@ -0,0 +1,5 @@ +fun f() { + val v = 239 + println(v) + println(v + v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/MultipleUsages.kt.after b/idea/testData/refactoring/inline/MultipleUsages.kt.after new file mode 100644 index 00000000000..017f4d4250d --- /dev/null +++ b/idea/testData/refactoring/inline/MultipleUsages.kt.after @@ -0,0 +1,4 @@ +fun f() { + println(239) + println(239 + 239) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/Parameter.kt b/idea/testData/refactoring/inline/Parameter.kt new file mode 100644 index 00000000000..6bb5e5854f8 --- /dev/null +++ b/idea/testData/refactoring/inline/Parameter.kt @@ -0,0 +1,3 @@ +fun f(val p: Int) { + println(p) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/ValWithoutInitializer.kt b/idea/testData/refactoring/inline/ValWithoutInitializer.kt new file mode 100644 index 00000000000..72a2abc129c --- /dev/null +++ b/idea/testData/refactoring/inline/ValWithoutInitializer.kt @@ -0,0 +1,4 @@ +fun f() { + val v: Int + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/Var.kt b/idea/testData/refactoring/inline/Var.kt new file mode 100644 index 00000000000..2717803d0ea --- /dev/null +++ b/idea/testData/refactoring/inline/Var.kt @@ -0,0 +1,4 @@ +fun f() { + var v = 239 + println(v) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java new file mode 100644 index 00000000000..7c511cd162b --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java @@ -0,0 +1,41 @@ +package org.jetbrains.jet.plugin.refactoring.inline; + +import com.intellij.codeInsight.TargetElementUtilBase; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.psi.PsiElement; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; + +import static com.intellij.codeInsight.TargetElementUtilBase.ELEMENT_NAME_ACCEPTED; +import static com.intellij.codeInsight.TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED; + +public abstract class AbstractInlineTest extends LightCodeInsightFixtureTestCase { + protected void doTest(@NotNull String path) throws IOException { + File afterFile = new File(path + ".after"); + + myFixture.configureByFile(path); + + boolean afterFileExists = afterFile.exists(); + + final PsiElement targetElement = + TargetElementUtilBase.findTargetElement(myFixture.getEditor(), ELEMENT_NAME_ACCEPTED | REFERENCED_ELEMENT_ACCEPTED); + final KotlinInlineLocalHandler handler = new KotlinInlineLocalHandler(); + + assertEquals(afterFileExists, handler.canInlineElement(targetElement)); + if (!afterFileExists) { + return; + } + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + handler.inlineElement(myFixture.getProject(), myFixture.getEditor(), targetElement); + } + }); + myFixture.checkResult(FileUtil.loadFile(afterFile)); + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java new file mode 100644 index 00000000000..439fb260649 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2013 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.jet.plugin.refactoring.inline; + +import junit.framework.Assert; +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.io.File; +import java.util.regex.Pattern; +import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.InnerTestClasses; +import org.jetbrains.jet.test.TestMetadata; + +import org.jetbrains.jet.plugin.refactoring.inline.AbstractInlineTest; + +/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/refactoring/inline") +public class InlineTestGenerated extends AbstractInlineTest { + public void testAllFilesPresentInInline() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/refactoring/inline"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("Basic.kt") + public void testBasic() throws Exception { + doTest("idea/testData/refactoring/inline/Basic.kt"); + } + + @TestMetadata("BasicCaretOnDeclaration.kt") + public void testBasicCaretOnDeclaration() throws Exception { + doTest("idea/testData/refactoring/inline/BasicCaretOnDeclaration.kt"); + } + + @TestMetadata("InFunctionLiteral.kt") + public void testInFunctionLiteral() throws Exception { + doTest("idea/testData/refactoring/inline/InFunctionLiteral.kt"); + } + + @TestMetadata("MethodReference.kt") + public void testMethodReference() throws Exception { + doTest("idea/testData/refactoring/inline/MethodReference.kt"); + } + + @TestMetadata("MultipleUsages.kt") + public void testMultipleUsages() throws Exception { + doTest("idea/testData/refactoring/inline/MultipleUsages.kt"); + } + + @TestMetadata("Parameter.kt") + public void testParameter() throws Exception { + doTest("idea/testData/refactoring/inline/Parameter.kt"); + } + + @TestMetadata("ValWithoutInitializer.kt") + public void testValWithoutInitializer() throws Exception { + doTest("idea/testData/refactoring/inline/ValWithoutInitializer.kt"); + } + + @TestMetadata("Var.kt") + public void testVar() throws Exception { + doTest("idea/testData/refactoring/inline/Var.kt"); + } + +}