From 5b1f3080d37c2eb38d063e1256133fab3583d8cb Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Sat, 16 May 2015 22:09:04 +0300 Subject: [PATCH] Add quickfix removing names of function expressions --- .../idea/quickfix/QuickFixRegistrar.java | 3 + .../RemoveNameFromFunctionExpressionFix.kt | 83 +++++++++++++++++++ .../removeNameFromFunctionExpression/basic.kt | 7 ++ .../basic.kt.after | 7 ++ .../manyFilesMuitliple.after.data.Sample.kt | 3 + .../manyFilesMuitliple.after.kt | 46 ++++++++++ .../manyFilesMuitliple.before.Main.kt | 46 ++++++++++ .../manyFilesMuitliple.before.data.Sample.kt | 3 + .../QuickFixMultiFileTestGenerated.java | 15 ++++ .../idea/quickfix/QuickFixTestGenerated.java | 15 ++++ 10 files changed, 228 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt.after create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.data.Sample.kt create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.kt create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.Main.kt create mode 100644 idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.data.Sample.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 2ce59f81604..94251e29561 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -337,5 +337,8 @@ public class QuickFixRegistrar { QuickFixes.factories.put(MISSING_CONSTRUCTOR_KEYWORD, MissingConstructorKeywordFix.Companion); QuickFixes.factories.put(MISSING_CONSTRUCTOR_KEYWORD, MissingConstructorKeywordFix.Companion.createWholeProjectFixFactory()); + + QuickFixes.factories.put(FUNCTION_EXPRESSION_WITH_NAME, RemoveNameFromFunctionExpressionFix.Companion); + QuickFixes.factories.put(FUNCTION_EXPRESSION_WITH_NAME, RemoveNameFromFunctionExpressionFix.Companion.createWholeProjectFixFactory()); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt new file mode 100644 index 00000000000..18bd5f19594 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveNameFromFunctionExpressionFix.kt @@ -0,0 +1,83 @@ +/* + * 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.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiWhiteSpace +import org.jetbrains.kotlin.JetNodeTypes +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.resolve.BindingContext + +public class RemoveNameFromFunctionExpressionFix(element: JetNamedFunction) : JetIntentionAction(element) { + override fun getText(): String = "Remove identifier from function expression" + override fun getFamilyName(): String = getText() + + override fun invoke(project: Project, editor: Editor?, file: JetFile) = removeNameFromFunction(element) + + companion object : JetSingleIntentionActionFactory() { + + override fun createAction(diagnostic: Diagnostic) = + diagnostic.createIntentionForFirstParentOfType(::RemoveNameFromFunctionExpressionFix) + + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createByPredicate( + predicate = { isFunctionExpression(it) }, + taskProcessor = { removeNameFromFunction(it) }, + modalTitle = "Removing identifier from function expressions", + name = "Remove identifier from function expressions in the whole project", + familyName = "Remove identifier from function expressions in the whole project" + ) + } + + private fun isFunctionExpression(function: JetNamedFunction): Boolean { + var parent = function.getParent() + + while (parent is JetAnnotatedExpression || parent is JetLabeledExpression) { + parent = parent.getParent() + } + + return function.isLocal() && parent !is JetBlockExpression + } + + private fun removeNameFromFunction(function: JetNamedFunction) { + var wereAutoLabelUsages = false + val name = function.getName() ?: return + + function.forEachDescendantsOfType { + if (!wereAutoLabelUsages && it.getLabelName() == name) { + wereAutoLabelUsages = it.analyze().get(BindingContext.LABEL_TARGET, it.getTargetLabel()) == function + } + } + + function.getNameIdentifier()?.delete() + + if (wereAutoLabelUsages) { + val psiFactory = JetPsiFactory(function) + val newFunction = psiFactory.createExpressionByPattern("$name@ $0", function) + function.replace(newFunction) + } + } + } +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt new file mode 100644 index 00000000000..3d966df7421 --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt @@ -0,0 +1,7 @@ +// "Remove identifier from function expression" "true" + +fun foo() { + (fun bar() { + return@bar + }) +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt.after b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt.after new file mode 100644 index 00000000000..d1054076742 --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt.after @@ -0,0 +1,7 @@ +// "Remove identifier from function expression" "true" + +fun foo() { + (bar@ fun() { + return@bar + }) +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.data.Sample.kt b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.data.Sample.kt new file mode 100644 index 00000000000..317eb0defd2 --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.data.Sample.kt @@ -0,0 +1,3 @@ +fun main() { + (fun() {}) +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.kt b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.kt new file mode 100644 index 00000000000..acf8277eaa4 --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.after.kt @@ -0,0 +1,46 @@ +// "Remove identifier from function expressions in the whole project" "true" + +inline fun run(block: () -> Unit) = block() +annotation class ann + +fun foo() { + l2@ @ann l2@ fun local() { + run(l1@ fun() { return@l1 }) + + run(label@ expr@ fun() { + return@label + return@expr + }) + } +} + +class A { + fun bar() { + run(toRun@ fun() { + if (1 == 1) return@toRun + return@bar + }) + + run( + /* abc */ fun /* cde */ () { + return@bar + } + ) + + run( + /* abc */ + foo@ fun /* cde */ () { + return@foo + } + ) + } + + init { + (foo@ fun A.() { + (fun() { + val x = 1 + }) + return@foo + }) + } +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.Main.kt b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.Main.kt new file mode 100644 index 00000000000..f8d30ca77d1 --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.Main.kt @@ -0,0 +1,46 @@ +// "Remove identifier from function expressions in the whole project" "true" + +inline fun run(block: () -> Unit) = block() +annotation class ann + +fun foo() { + l2@ @ann l2@ fun local() { + run(l1@ fun() { return@l1 }) + + run(label@ fun expr() { + return@label + return@expr + }) + } +} + +class A { + fun bar() { + run(fun toRun() { + if (1 == 1) return@toRun + return@bar + }) + + run( + /* abc */ fun /* cde */ toRun() { + return@bar + } + ) + + run( + /* abc */ + fun /* cde */ foo() { + return@foo + } + ) + } + + init { + (fun A.foo() { + (fun bar() { + val x = 1 + }) + return@foo + }) + } +} diff --git a/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.data.Sample.kt b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.data.Sample.kt new file mode 100644 index 00000000000..9917038de9d --- /dev/null +++ b/idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.data.Sample.kt @@ -0,0 +1,3 @@ +fun main() { + (fun foo() {}) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index c933f3486de..acc5a736047 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -918,6 +918,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/migration/removeNameFromFunctionExpression") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RemoveNameFromFunctionExpression extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInRemoveNameFromFunctionExpression() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/removeNameFromFunctionExpression"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); + } + + @TestMetadata("manyFilesMuitliple.before.Main.kt") + public void testManyFilesMuitliple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/removeNameFromFunctionExpression/manyFilesMuitliple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/replaceJavaClassAsAnnotationParameter") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 77afa7a2b4a..587bf5e0292 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3295,6 +3295,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/migration/removeNameFromFunctionExpression") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RemoveNameFromFunctionExpression extends AbstractQuickFixTest { + public void testAllFilesPresentInRemoveNameFromFunctionExpression() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/removeNameFromFunctionExpression"), Pattern.compile("^(\\w+)\\.kt$"), true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/removeNameFromFunctionExpression/basic.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/replaceJavaClassAsAnnotationParameter") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)