From c170c7b98f16946bc184373592cb7f855d3c923d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 27 Jan 2017 19:45:41 +0100 Subject: [PATCH] Add quickfix to enable coroutines by editing build.gradle #KT-15955 Fixed --- .../KotlinWithGradleConfigurator.kt | 39 ++++++++++++ .../quickfix/ChangeCoroutineSupportFix.kt | 17 +++++- .../KotlinWithGradleConfiguratorTest.kt | 61 +++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 idea/tests/org/jetbrains/kotlin/gradle/KotlinWithGradleConfiguratorTest.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt index a183a62bc40..ad1f1fccdf6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt @@ -40,8 +40,10 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.groovy.lang.psi.GroovyFile import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner import java.io.File @@ -255,6 +257,43 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { } } + fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? { + return changeBuildGradle(module) { gradleFile -> + changeCoroutineConfiguration(gradleFile, coroutineOption) + } + } + + fun changeCoroutineConfiguration(gradleFile: GroovyFile, coroutineOption: String): PsiElement? { + val snippet = "coroutines \"$coroutineOption\"" + val kotlinBlock = getBlockOrCreate(gradleFile, "kotlin") + val experimentalBlock = getBlockOrCreate(kotlinBlock, "experimental") + addOrReplaceExpression(experimentalBlock, snippet) { stmt -> + (stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines" + } + return kotlinBlock.parent + } + + private fun changeBuildGradle(module: Module, body: (GroovyFile) -> PsiElement?): PsiElement? { + val gradleFilePath = getModuleFilePath(module) + val gradleFile = getBuildGradleFile(module.project, gradleFilePath) + if (gradleFile != null && canConfigureFile(gradleFile)) { + return gradleFile.project.executeWriteCommand("Change build.gradle configuration", null) { + body(gradleFile) + } + } + return null + } + + private fun addOrReplaceExpression(block: GrClosableBlock, snippet: String, predicate: (GrStatement) -> Boolean) { + block.statements.firstOrNull(predicate)?.let { stmt -> + val newStatement = GroovyPsiElementFactory.getInstance(block.project).createExpressionFromText(snippet) + CodeStyleManager.getInstance(block.project).reformat(newStatement) + stmt.replaceWithStatement(newStatement) + return + } + addLastExpressionInBlockIfNeeded(snippet, block) + } + fun getKotlinStdlibVersion(module: Module): String? { val gradleFilePath = getModuleFilePath(module) val gradleFile = getBuildGradleFile(module.project, gradleFilePath) ?: return null diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt index 9933836f196..1d47fe1d4a6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootModificationUtil @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder +import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.psi.KtFile @@ -41,6 +43,14 @@ sealed class ChangeCoroutineSupportFix( override fun invoke(project: Project, editor: Editor?, file: KtFile) { val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return + if (KotlinPluginUtil.isGradleModule(module)) { + val element = KotlinWithGradleConfigurator.changeCoroutineConfiguration(module, coroutineSupport.compilerArgument) + element?.let { + OpenFileDescriptor(project, it.containingFile.virtualFile, it.textRange.startOffset).navigate(true) + } + return + } + val facetSettings = KotlinFacet.get(module)?.configuration?.settings ?: return ModuleRootModificationUtil.updateModel(module) { facetSettings.compilerInfo.coroutineSupport = coroutineSupport @@ -85,10 +95,13 @@ sealed class ChangeCoroutineSupportFix( else -> return emptyList() } val module = ModuleUtilCore.findModuleForPsiElement(diagnostic.psiElement) ?: return emptyList() - if (KotlinPluginUtil.isGradleModule(module) || KotlinPluginUtil.isMavenModule(module)) return emptyList() + if (KotlinPluginUtil.isMavenModule(module)) return emptyList() val facetSettings = KotlinFacet.get(module)?.configuration?.settings + + val configureInProject = (facetSettings == null || facetSettings.useProjectSettings) && + !KotlinPluginUtil.isGradleModule(module) val quickFixConstructor: (PsiElement, CoroutineSupport) -> ChangeCoroutineSupportFix = - if (facetSettings == null || facetSettings.useProjectSettings) ::InProject else ::InModule + if (configureInProject) ::InProject else ::InModule return newCoroutineSupports.map { quickFixConstructor(diagnostic.psiElement, it) } } } diff --git a/idea/tests/org/jetbrains/kotlin/gradle/KotlinWithGradleConfiguratorTest.kt b/idea/tests/org/jetbrains/kotlin/gradle/KotlinWithGradleConfiguratorTest.kt new file mode 100644 index 00000000000..6a8b6179167 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/gradle/KotlinWithGradleConfiguratorTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2017 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.gradle + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile + +class KotlinWithGradleConfiguratorTest : LightCodeInsightFixtureTestCase() { + fun testAddCoroutinesSupport() { + val buildGradle = myFixture.configureByText("build.gradle", "apply plugin: 'kotlin'\n") as GroovyFile + myFixture.project.executeWriteCommand("") { + KotlinWithGradleConfigurator.changeCoroutineConfiguration(buildGradle, "enable") + } + myFixture.checkResult( + """apply plugin: 'kotlin' + |kotlin { + | experimental { + | coroutines "enable" + | } + |} + |""".trimMargin("|")) + } + + fun testChangeCoroutinesSupport() { + val buildGradle = myFixture.configureByText("build.gradle", + """apply plugin: 'kotlin' + |kotlin { + | experimental { + | coroutines "disable" + | } + |} + |""".trimMargin("|")) as GroovyFile + myFixture.project.executeWriteCommand("") { + KotlinWithGradleConfigurator.changeCoroutineConfiguration(buildGradle, "enable") + } + myFixture.checkResult( + """apply plugin: 'kotlin' + |kotlin { + | experimental { + | coroutines "enable" + | } + |} + |""".trimMargin("|")) + } +}