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 64e87c1774f..85e9efb99ca 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -512,3 +512,22 @@ public fun PsiElement.getElementTextWithContext(): String { .insert(0, "File name: ${getContainingFile().getName()}\n") .toString() } + +// Calls `block` on each descendant of T type +// Note, that calls happen in order of DFS-exit, so deeper nodes are applied earlier +inline fun forEachDescendantOfTypeVisitor( + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) block: (T) -> Unit +): JetVisitorVoid = + object : JetTreeVisitorVoid() { + override fun visitJetElement(element: JetElement) { + super.visitJetElement(element) + if (element is T) { + block(element) + } + } + } + +inline fun flatMapDescendantsOfTypeVisitor( + accumulator: MutableCollection, + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) map: (T) -> Collection +): JetVisitorVoid = forEachDescendantOfTypeVisitor { accumulator.addAll(map(it)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddInitKeywordFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddInitKeywordFix.kt index 6e58f9e7281..56458826ef1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddInitKeywordFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddInitKeywordFix.kt @@ -25,6 +25,7 @@ import com.intellij.openapi.project.* import com.intellij.openapi.editor.* import com.intellij.psi.* import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.psiUtil.* @@ -41,7 +42,19 @@ public class AddInitKeywordFix(element: JetClassInitializer) : JetIntentionActio } companion object Factory : JetSingleIntentionActionFactory() { - fun addInitKeyword(element: JetClassInitializer) { + override fun createAction(diagnostic: Diagnostic) = diagnostic.createIntentionForFirstParentOfType(::AddInitKeywordFix) + + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createByPredicate( + predicate = { !it.hasInitKeyword() }, + taskProcessor = { addInitKeyword(it) }, + modalTitle = JetBundle.message("add.init.keyword.in.whole.project.modal.title"), + name = JetBundle.message("add.init.keyword.in.whole.project"), + familyName = JetBundle.message("add.init.keyword.in.whole.project.family") + ) + } + + private fun addInitKeyword(element: JetClassInitializer) { if (element.hasInitKeyword()) return val psiFactory = JetPsiFactory(element) @@ -55,39 +68,5 @@ public class AddInitKeywordFix(element: JetClassInitializer) : JetIntentionActio prevLeaf!!.delete() } } - override fun createAction(diagnostic: Diagnostic) = diagnostic.createIntentionForFirstParentOfType(::AddInitKeywordFix) - } -} - -public class AddInitKeywordFixInWholeProjectFix(elem: JetClassInitializer) -: JetWholeProjectModalAction>( - elem, JetBundle.message("add.init.keyword.in.whole.project.modal.title")) { - override fun getText(): String = JetBundle.message("add.init.keyword.in.whole.project") - - override fun getFamilyName(): String = JetBundle.message("add.init.keyword.in.whole.project.family") - - override fun collectDataForFile(project: Project, file: JetFile): Collection? { - val classInitializers = ArrayList() - file.accept(AddInitKeywordVisitor(classInitializers)) - - return if (classInitializers.isEmpty()) null else classInitializers - } - - override fun applyChangesForFile(project: Project, file: JetFile, data: Collection) { - data.forEach { AddInitKeywordFix.addInitKeyword(it) } - } - - private class AddInitKeywordVisitor(val classInitializers: MutableCollection) : JetTreeVisitorVoid() { - override fun visitAnonymousInitializer(initializer: JetClassInitializer) { - initializer.acceptChildren(this) - if (!initializer.hasInitKeyword()) { - classInitializers.add(initializer) - } - } - } - - companion object Factory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic) = - diagnostic.createIntentionForFirstParentOfType(::AddInitKeywordFixInWholeProjectFix) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ClassObjectToDefaultObjectFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ClassObjectToDefaultObjectFix.kt index fa495d53d58..4edefe557ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ClassObjectToDefaultObjectFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ClassObjectToDefaultObjectFix.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* import java.util.ArrayList @@ -31,14 +32,24 @@ public class ClassObjectToCompanionObjectFix(private val elem: JetObjectDeclarat override fun getFamilyName(): String = JetBundle.message("migrate.class.object.to.companion.family") override fun invoke(project: Project, editor: Editor, file: JetFile) { - classKeywordToCompanionModifier(elem) + changeClassKeywordToCompanionModifier(elem) } companion object Factory : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic) = (diagnostic.getPsiElement() as? JetObjectDeclaration)?.let { ClassObjectToCompanionObjectFix(it) } - fun classKeywordToCompanionModifier(objectDeclaration: JetObjectDeclaration) { + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createByPredicate( + predicate = { it.getClassKeyword() != null }, + taskProcessor = { changeClassKeywordToCompanionModifier(it) }, + modalTitle = JetBundle.message("migrate.class.object.to.companion.in.whole.project.modal.title"), + name = JetBundle.message("migrate.class.object.to.companion.in.whole.project"), + familyName = JetBundle.message("migrate.class.object.to.companion.in.whole.project.family") + ) + } + + private fun changeClassKeywordToCompanionModifier(objectDeclaration: JetObjectDeclaration) { objectDeclaration.getClassKeyword()?.delete() if (!objectDeclaration.hasModifier(JetTokens.COMPANION_KEYWORD)) { objectDeclaration.addModifier(JetTokens.COMPANION_KEYWORD) @@ -46,36 +57,3 @@ public class ClassObjectToCompanionObjectFix(private val elem: JetObjectDeclarat } } } - -public class ClassObjectToCompanionObjectInWholeProjectFix(private val elem: JetObjectDeclaration) - : JetWholeProjectModalAction>( - elem, JetBundle.message("migrate.class.object.to.companion.in.whole.project.modal.title")) { - override fun getText(): String = JetBundle.message("migrate.class.object.to.companion.in.whole.project") - - override fun getFamilyName(): String = JetBundle.message("migrate.class.object.to.companion.in.whole.project.family") - - override fun collectDataForFile(project: Project, file: JetFile): Collection? { - val classObjects = ArrayList() - file.accept(ClassObjectToCompanionObjectVisitor(classObjects)) - - return if (classObjects.isEmpty()) null else classObjects - } - - override fun applyChangesForFile(project: Project, file: JetFile, data: Collection) { - data.forEach { ClassObjectToCompanionObjectFix.classKeywordToCompanionModifier(it) } - } - - private class ClassObjectToCompanionObjectVisitor(val classObjects: MutableCollection) : JetTreeVisitorVoid() { - override fun visitObjectDeclaration(objectDeclaration: JetObjectDeclaration) { - objectDeclaration.acceptChildren(this) - if (objectDeclaration.getClassKeyword() != null) { - classObjects.add(objectDeclaration) - } - } - } - - companion object Factory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic) = - (diagnostic.getPsiElement() as? JetObjectDeclaration)?.let { ClassObjectToCompanionObjectInWholeProjectFix(it) } - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt index 43588679e38..ccade7ab533 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedLambdaSyntaxFix.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.psiModificationUtil.getFunctionLiteralArgumentName @@ -30,6 +31,7 @@ import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveInsideParenthesesA import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.utils.sure import java.util.ArrayList public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : JetIntentionAction(element) { @@ -43,64 +45,45 @@ public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : companion object Factory : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic) = (diagnostic.getPsiElement() as? JetFunctionLiteralExpression)?.let { DeprecatedLambdaSyntaxFix(it) } - } -} -public class DeprecatedLambdaSyntaxInWholeProjectFix(element: JetFunctionLiteralExpression) : - JetWholeProjectModalAction>( - element, JetBundle.message("migrate.lambda.syntax.in.whole.project.modal.title")) { - - override fun getText() = JetBundle.message("migrate.lambda.syntax.in.whole.project") - override fun getFamilyName() = JetBundle.message("migrate.lambda.syntax.in.whole.project.family") - - override fun collectDataForFile(project: Project, file: JetFile): Collection? { - val lambdas = ArrayList() - file.accept(LambdaCollectionVisitor(lambdas), 0) - return lambdas.sortBy { -it.level } - } - - override fun applyChangesForFile(project: Project, file: JetFile, data: Collection) { - data.forEach { - it.runFix() + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createByTaskFactory( + taskFactory = fun (it: JetFunctionLiteralExpression) = fixTaskFactory(it), + taskProcessor = { it.runFix() }, + modalTitle = JetBundle.message("migrate.lambda.syntax.in.whole.project.modal.title"), + name = JetBundle.message("migrate.lambda.syntax.in.whole.project"), + familyName = JetBundle.message("migrate.lambda.syntax.in.whole.project.family") + ) } - } - private class LambdaCollectionVisitor(val lambdas: MutableCollection) : JetTreeVisitor() { - override fun visitFunctionLiteralExpression(functionLiteralExpression: JetFunctionLiteralExpression, data: Int): Void? { - functionLiteralExpression.acceptChildren(this, data + 1) - if (JetPsiUtil.isDeprecatedLambdaSyntax(functionLiteralExpression)) { - lambdas.add(DeprecatedSyntaxFix.createFix(functionLiteralExpression, data)) + private fun fixTaskFactory(functionLiteralExpression: JetFunctionLiteralExpression): DeprecatedSyntaxFix? { + return if (JetPsiUtil.isDeprecatedLambdaSyntax(functionLiteralExpression)) { + DeprecatedSyntaxFix.createFix(functionLiteralExpression) + } + else { + null } - return null } } - - companion object Factory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic) - = (diagnostic.getPsiElement() as? JetFunctionLiteralExpression)?.let { DeprecatedLambdaSyntaxInWholeProjectFix(it) } - } } private trait DeprecatedSyntaxFix { - val level: Int - // you must run it under write action fun runFix() internal companion object { - fun createFix(functionLiteralExpression: JetFunctionLiteralExpression, level: Int = 0): DeprecatedSyntaxFix { + fun createFix(functionLiteralExpression: JetFunctionLiteralExpression): DeprecatedSyntaxFix { val functionLiteral = functionLiteralExpression.getFunctionLiteral() val hasNoReturnAndReceiverType = !functionLiteral.hasDeclaredReturnType() && functionLiteral.getReceiverTypeReference() == null - return if (hasNoReturnAndReceiverType) DeparenthesizeParameterList(functionLiteralExpression, level) - else LambdaToFunctionExpression(functionLiteralExpression, level) + return if (hasNoReturnAndReceiverType) DeparenthesizeParameterList(functionLiteralExpression) + else LambdaToFunctionExpression(functionLiteralExpression) } } } private class DeparenthesizeParameterList( - val functionLiteralExpression: JetFunctionLiteralExpression, - override val level: Int = 0 + val functionLiteralExpression: JetFunctionLiteralExpression ): DeprecatedSyntaxFix { override fun runFix() { @@ -118,8 +101,7 @@ private class DeparenthesizeParameterList( } private class LambdaToFunctionExpression( - val functionLiteralExpression: JetFunctionLiteralExpression, - override val level: Int = 0 + val functionLiteralExpression: JetFunctionLiteralExpression ): DeprecatedSyntaxFix { val functionLiteralArgumentName: String? val receiverType: String? diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/JetWholeProjectModalAction.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/JetWholeProjectModalAction.kt index 9f49f7c680a..feb7f2bc304 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/JetWholeProjectModalAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/JetWholeProjectModalAction.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.quickfix +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Editor @@ -25,18 +26,28 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.util.ui.UIUtil import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetVisitor +import org.jetbrains.kotlin.psi.JetVisitorVoid +import org.jetbrains.kotlin.psi.psiUtil.flatMapDescendantsOfTypeVisitor +import org.jetbrains.kotlin.utils.singletonOrEmptyList import java.util.HashMap -public abstract class JetWholeProjectModalAction(element: T, val title: String) : JetIntentionAction(element) { +public abstract class JetWholeProjectModalAction(val title: String) : IntentionAction { override final fun startInWriteAction() = false - override final fun invoke(project: Project, editor: Editor, file: JetFile) = + override final fun invoke(project: Project, editor: Editor?, file: PsiFile?) = invoke(project) + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?) = true + + private fun invoke(project: Project) = ProgressManager.getInstance().run( object : Task.Modal(project, title, true) { override fun run(indicator: ProgressIndicator) { @@ -84,7 +95,78 @@ public abstract class JetWholeProjectModalAction(element protected abstract fun applyChangesForFile(project: Project, file: JetFile, data: D) private companion object { - val LOG = Logger.getInstance(javaClass>()); + val LOG = Logger.getInstance(javaClass>()); } } +public abstract class JetWholeProjectModalByCollectionAction(modalTitle: String) +: JetWholeProjectModalAction>(modalTitle) { + override fun collectDataForFile(project: Project, file: JetFile): Collection? { + val accumulator = arrayListOf() + collectTasksForFile(project, file, accumulator) + return accumulator + } + + abstract fun collectTasksForFile(project: Project, file: JetFile, accumulator: MutableCollection) +} + +class JetWholeProjectForEachElementOfTypeFix private ( + val collectingVisitorFactory: (MutableCollection) -> JetVisitorVoid, + val tasksProcessor: (Collection) -> Unit, + modalTitle: String, + val name: String, + val familyNameText: String +) : JetWholeProjectModalByCollectionAction(modalTitle) { + + override fun getFamilyName() = familyNameText + override fun getText() = name + + override fun collectTasksForFile(project: Project, file: JetFile, accumulator: MutableCollection) { + file.accept(collectingVisitorFactory(accumulator)) + } + override fun applyChangesForFile(project: Project, file: JetFile, data: Collection) = tasksProcessor(data) + + companion object { + inline fun createByPredicate( + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) predicate: (E) -> Boolean, + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) taskProcessor: (E) -> Unit, + modalTitle: String, + name: String, + familyName: String + ) = createByTaskFactory( + taskFactory = { if (predicate(it)) it else null }, + taskProcessor = taskProcessor, + modalTitle = modalTitle, + name = name, + familyName = familyName + ) + + inline fun createByTaskFactory( + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) taskFactory: (E) -> D?, + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) taskProcessor: (D) -> Unit, + modalTitle: String, + name: String, + familyName: String + ) = createForMultiTask( + tasksFactory = { taskFactory(it).singletonOrEmptyList() }, + tasksProcessor = { it.forEach(taskProcessor) }, + modalTitle = modalTitle, + name = name, + familyName = familyName + ) + + inline fun createForMultiTask( + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) tasksFactory: (E) -> Collection, + noinline tasksProcessor: (Collection) -> Unit, + modalTitle: String, + name: String, + familyName: String + ) = JetWholeProjectForEachElementOfTypeFix( + collectingVisitorFactory = { accumulator -> flatMapDescendantsOfTypeVisitor(accumulator, tasksFactory) }, + tasksProcessor = tasksProcessor, + modalTitle = modalTitle, + name = name, + familyNameText = familyName + ) + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 714c1ad1a4a..24adcf315e3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateL import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterActionFactory; import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory; import org.jetbrains.kotlin.idea.quickfix.replaceJavaClass.ReplaceJavaClassAsAnnotationArgumentFix; -import org.jetbrains.kotlin.idea.quickfix.replaceJavaClass.ReplaceJavaClassAsAnnotationArgumentInWholeProjectFix; import org.jetbrains.kotlin.psi.JetClass; import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm; @@ -236,7 +235,7 @@ public class QuickFixRegistrar { QuickFixes.factories.put(UNUSED_PARAMETER, ChangeFunctionSignatureFix.createFactoryForUnusedParameter()); QuickFixes.factories.put(EXPECTED_PARAMETERS_NUMBER_MISMATCH, ChangeFunctionSignatureFix.createFactoryForParametersNumberMismatch()); QuickFixes.factories.put(DEPRECATED_LAMBDA_SYNTAX, DeprecatedLambdaSyntaxFix.Factory); - QuickFixes.factories.put(DEPRECATED_LAMBDA_SYNTAX, DeprecatedLambdaSyntaxInWholeProjectFix.Factory); + QuickFixes.factories.put(DEPRECATED_LAMBDA_SYNTAX, DeprecatedLambdaSyntaxFix.Factory.createWholeProjectFixFactory()); QuickFixes.factories.put(EXPECTED_PARAMETER_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedParameterTypeMismatch()); QuickFixes.factories.put(EXPECTED_RETURN_TYPE_MISMATCH, ChangeTypeFix.createFactoryForExpectedReturnTypeMismatch()); @@ -310,9 +309,9 @@ public class QuickFixRegistrar { QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromCallWithConstructorCalleeActionFactory.INSTANCE$); QuickFixes.factories.put(DEPRECATED_CLASS_OBJECT_SYNTAX, ClassObjectToCompanionObjectFix.Factory); - QuickFixes.factories.put(DEPRECATED_CLASS_OBJECT_SYNTAX, ClassObjectToCompanionObjectInWholeProjectFix.Factory); + QuickFixes.factories.put(DEPRECATED_CLASS_OBJECT_SYNTAX, ClassObjectToCompanionObjectFix.Factory.createWholeProjectFixFactory()); QuickFixes.factories.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, AddInitKeywordFix.Factory); - QuickFixes.factories.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, AddInitKeywordFixInWholeProjectFix.Factory); + QuickFixes.factories.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, AddInitKeywordFix.Factory.createWholeProjectFixFactory()); QuickFixes.factories.put(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED, InsertDelegationCallQuickfix.InsertThisDelegationCallFactory.INSTANCE$); @@ -320,6 +319,6 @@ public class QuickFixRegistrar { QuickFixes.factories.put(EXPLICIT_DELEGATION_CALL_REQUIRED, InsertDelegationCallQuickfix.InsertSuperDelegationCallFactory.INSTANCE$); QuickFixes.factories.put(ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION, ReplaceJavaClassAsAnnotationArgumentFix.Companion); - QuickFixes.factories.put(ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION, ReplaceJavaClassAsAnnotationArgumentInWholeProjectFix.Companion); + QuickFixes.factories.put(ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION, ReplaceJavaClassAsAnnotationArgumentFix.Companion.createWholeProjectFixFactory()); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil.kt index 3a72d214d12..9a288114901 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/quickfixUtil.kt @@ -16,11 +16,20 @@ package org.jetbrains.kotlin.idea.quickfix.quickfixUtil +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.PsiElement import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction +import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType inline fun Diagnostic.createIntentionForFirstParentOfType( factory: (T) -> JetIntentionAction? ) = getPsiElement().getNonStrictParentOfType()?.let(factory) + + +inline fun createIntentionFactory( + inlineOptions(InlineOption.ONLY_LOCAL_RETURN) factory: (Diagnostic) -> IntentionAction? +) = object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic) = factory(diagnostic) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/ReplaceJavaClassAsAnnotationArgumentFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/ReplaceJavaClassAsAnnotationArgumentFix.kt index ff355b55adf..498cb04c8da 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/ReplaceJavaClassAsAnnotationArgumentFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/ReplaceJavaClassAsAnnotationArgumentFix.kt @@ -22,9 +22,8 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.JetBundle -import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction -import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory -import org.jetbrains.kotlin.idea.quickfix.JetWholeProjectModalAction +import org.jetbrains.kotlin.idea.quickfix.* +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences @@ -36,50 +35,25 @@ public class ReplaceJavaClassAsAnnotationArgumentFix( annotationEntry: JetAnnotationEntry ) : JetIntentionAction(annotationEntry) { - private val psiFactory: JetPsiFactory = JetPsiFactory(annotationEntry) - override fun getText() = JetBundle.message("replace.java.class.argument") override fun getFamilyName() = JetBundle.message("replace.java.class.argument.family") override fun invoke(project: Project, editor: Editor?, file: JetFile?) { - processTasks(createReplacementTasks(element), psiFactory) + processTasks(createReplacementTasks(element)) } companion object : JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic) = diagnostic.createIntentionForFirstParentOfType(::ReplaceJavaClassAsAnnotationArgumentFix) - } -} - -public class ReplaceJavaClassAsAnnotationArgumentInWholeProjectFix( - annotationEntry: JetAnnotationEntry -) : JetWholeProjectModalAction>( - annotationEntry, JetBundle.message("replace.java.class.argument.in.whole.project.modal.title") -) { - - private val psiFactory: JetPsiFactory = JetPsiFactory(annotationEntry) - - override fun getText() = JetBundle.message("replace.java.class.argument.in.whole.project") - override fun getFamilyName() = JetBundle.message("replace.java.class.argument.in.whole.project.family") - - override fun collectDataForFile(project: Project, file: JetFile): Collection? { - val result = arrayListOf() - - file.accept(object : JetTreeVisitorVoid() { - override fun visitAnnotationEntry(annotationEntry: JetAnnotationEntry) { - result.addAll(createReplacementTasks(annotationEntry)) - } - }) - - return result - } - - override fun applyChangesForFile(project: Project, file: JetFile, data: Collection) { - processTasks(data, psiFactory) - } - - companion object : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic) = - diagnostic.createIntentionForFirstParentOfType(::ReplaceJavaClassAsAnnotationArgumentInWholeProjectFix) + + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createForMultiTask( + tasksFactory = ::createReplacementTasks, + tasksProcessor = ::processTasks, + modalTitle = JetBundle.message("replace.java.class.argument.in.whole.project.modal.title"), + name = JetBundle.message("replace.java.class.argument.in.whole.project"), + familyName = JetBundle.message("replace.java.class.argument.in.whole.project.family") + ) + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt index cf290d3880b..12c6bb1c76a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceJavaClass/common.kt @@ -28,11 +28,15 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.types.typeUtil.isArrayOfJavaLangClass import org.jetbrains.kotlin.types.typeUtil.isJavaLangClass -private trait ReplacementTask +private trait ReplacementTask { + val element: JetElement +} private class JavaClassCallReplacementTask( val javaClassCall: JetExpression, val className: String -) : ReplacementTask +) : ReplacementTask { + override val element: JetElement = javaClassCall +} fun createReplacementTasks(element: JetAnnotationEntry): List { val replacementTasks = arrayListOf() @@ -63,7 +67,10 @@ private fun Diagnostic.isJavaLangClassArgumentInAnnotation(expression: JetCallEx getFactory() == ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION && getPsiElement().isAncestor(expression) -fun processTasks(replacementTasks: Collection, psiFactory: JetPsiFactory) { +fun processTasks(replacementTasks: Collection) { + val element = replacementTasks.firstOrNull()?.element ?: return + val psiFactory = JetPsiFactory(element) + val elementsToShorten = arrayListOf() replacementTasks.forEach { task ->