Move coroutine state update logic to KotlinProjectConfigurator

This commit is contained in:
Dmitry Jemerov
2017-08-18 15:36:22 +02:00
parent e781b70170
commit 6fbb370eae
9 changed files with 114 additions and 72 deletions
+1
View File
@@ -17,5 +17,6 @@
<orderEntry type="module" module-name="frontend.java" /> <orderEntry type="module" module-name="frontend.java" />
<orderEntry type="module" module-name="js.frontend" /> <orderEntry type="module" module-name="js.frontend" />
<orderEntry type="module" module-name="frontend.script" /> <orderEntry type="module" module-name="frontend.script" />
<orderEntry type="module" module-name="kotlin-gradle-tooling" />
</component> </component>
</module> </module>
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.ide.actions.OpenFileAction import com.intellij.ide.actions.OpenFileAction
import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtil import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -33,8 +34,12 @@ import com.intellij.openapi.vfs.WritingAccessProvider
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
@@ -177,6 +182,24 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return isModified return isModified
} }
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
if (runtimeUpdateRequired) {
Messages.showErrorDialog(module.project,
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
"Please update the version in your build script.",
ChangeCoroutineSupportFix.getFixText(state))
return
}
val element = changeCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state))
if (element != null) {
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
}
}
companion object { companion object {
fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) { fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) {
is KtFile -> KotlinBuildScriptManipulator(file) is KtFile -> KotlinBuildScriptManipulator(file)
@@ -20,11 +20,13 @@ import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.ide.actions.OpenFileAction import com.intellij.ide.actions.OpenFileAction
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.WritingAccessProvider import com.intellij.openapi.vfs.WritingAccessProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.psi.search.FileTypeIndex import com.intellij.psi.search.FileTypeIndex
@@ -35,12 +37,17 @@ import org.jetbrains.idea.maven.dom.model.MavenDomPlugin
import org.jetbrains.idea.maven.model.MavenId import org.jetbrains.idea.maven.model.MavenId
import org.jetbrains.idea.maven.project.MavenProjectsManager import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.idea.maven.utils.MavenArtifactScope import org.jetbrains.idea.maven.utils.MavenArtifactScope
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.configuration.* import org.jetbrains.kotlin.idea.configuration.*
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
import org.jetbrains.kotlin.idea.maven.PomFile import org.jetbrains.kotlin.idea.maven.PomFile
import org.jetbrains.kotlin.idea.maven.changeCoroutineConfiguration
import org.jetbrains.kotlin.idea.maven.excludeMavenChildrenModules import org.jetbrains.kotlin.idea.maven.excludeMavenChildrenModules
import org.jetbrains.kotlin.idea.maven.kotlinPluginId import org.jetbrains.kotlin.idea.maven.kotlinPluginId
import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
abstract class KotlinMavenConfigurator abstract class KotlinMavenConfigurator
@@ -187,6 +194,43 @@ abstract class KotlinMavenConfigurator
} }
} }
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
val messageTitle = ChangeCoroutineSupportFix.getFixText(state)
if (runtimeUpdateRequired) {
Messages.showErrorDialog(module.project,
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
"Please update the version in your build script.",
messageTitle)
return
}
val element = changeMavenCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(state), messageTitle)
if (element != null) {
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
}
}
private fun changeMavenCoroutineConfiguration(module: Module, value: String, messageTitle: String): PsiElement? {
fun doChangeMavenCoroutineConfiguration(): PsiElement? {
val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null
val pom = PomFile.forFileOrNull(psi) ?: return null
return pom.changeCoroutineConfiguration(value)
}
val element = doChangeMavenCoroutineConfiguration()
if (element == null) {
Messages.showErrorDialog(module.project,
"Failed to update.pom.xml. Please update the file manually.",
messageTitle)
}
return element
}
companion object { companion object {
val NAME = "maven" val NAME = "maven"
+1 -1
View File
@@ -67,7 +67,7 @@
<orderEntry type="module" module-name="idea-maven" /> <orderEntry type="module" module-name="idea-maven" />
<orderEntry type="module" module-name="backend.jvm" /> <orderEntry type="module" module-name="backend.jvm" />
<orderEntry type="library" name="kotlin-reflect" level="project" /> <orderEntry type="library" name="kotlin-reflect" level="project" />
<orderEntry type="module" module-name="idea-gradle" scope="TEST" /> <orderEntry type="module" module-name="idea-gradle" />
<orderEntry type="module" module-name="sam-with-receiver-cli" scope="TEST" /> <orderEntry type="module" module-name="sam-with-receiver-cli" scope="TEST" />
</component> </component>
</module> </module>
@@ -168,7 +168,7 @@ fun getConfigurableModules(project: Project): List<ModuleSourceRootGroup> {
} }
fun getAbleToRunConfigurators(module: Module): Collection<KotlinProjectConfigurator> { fun getAbleToRunConfigurators(module: Module): Collection<KotlinProjectConfigurator> {
val moduleGroup = ModuleSourceRootMap(module.project).toModuleGroup(module) val moduleGroup = module.toModuleGroup()
return allConfigurators().filter { return allConfigurators().filter {
it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED
} }
@@ -203,6 +203,12 @@ fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, excludeModules: C
}.map { it.baseModule } }.map { it.baseModule }
} }
fun findApplicableConfigurator(module: Module): KotlinProjectConfigurator {
val moduleGroup = module.toModuleGroup()
return allConfigurators().find { it.getStatus(moduleGroup) != ConfigureKotlinStatus.NON_APPLICABLE }
?: KotlinJavaModuleConfigurator.instance
}
fun hasAnyKotlinRuntimeInScope(module: Module): Boolean { fun hasAnyKotlinRuntimeInScope(module: Module): Boolean {
return runReadAction { return runReadAction {
val scope = module.getModuleWithDependenciesAndLibrariesScope(hasKotlinFilesOnlyInTests(module)) val scope = module.getModuleWithDependenciesAndLibrariesScope(hasKotlinFilesOnlyInTests(module))
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.TargetPlatform
enum class ConfigureKotlinStatus { enum class ConfigureKotlinStatus {
@@ -47,6 +48,8 @@ interface KotlinProjectConfigurator {
val targetPlatform: TargetPlatform val targetPlatform: TargetPlatform
fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State)
companion object { companion object {
val EP_NAME = ExtensionPointName.create<KotlinProjectConfigurator>("org.jetbrains.kotlin.projectConfigurator") val EP_NAME = ExtensionPointName.create<KotlinProjectConfigurator>("org.jetbrains.kotlin.projectConfigurator")
} }
@@ -28,9 +28,14 @@ import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VfsUtilCore
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
import org.jetbrains.kotlin.idea.framework.ui.CreateLibraryDialogWithModules import org.jetbrains.kotlin.idea.framework.ui.CreateLibraryDialogWithModules
import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils
import org.jetbrains.kotlin.idea.quickfix.askUpdateRuntime
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
import org.jetbrains.kotlin.idea.versions.findKotlinRuntimeLibrary import org.jetbrains.kotlin.idea.versions.findKotlinRuntimeLibrary
@@ -325,6 +330,23 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
!KotlinPluginUtil.isGradleModule(module) !KotlinPluginUtil.isGradleModule(module)
} }
override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) {
val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED &&
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
if (runtimeUpdateRequired && !askUpdateRuntime(module, LanguageFeature.Coroutines.sinceApiVersion)) {
return
}
val facetSettings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module)
ModuleRootModificationUtil.updateModel(module) {
facetSettings.coroutineSupport = state
facetSettings.apiLevel = LanguageVersion.KOTLIN_1_1
facetSettings.languageLevel = LanguageVersion.KOTLIN_1_1
}
}
companion object { companion object {
val DEFAULT_LIBRARY_DIR = "lib" val DEFAULT_LIBRARY_DIR = "lib"
@@ -58,6 +58,8 @@ class ModuleSourceRootMap(val modules: Collection<Module>) {
fun toModuleGroup(module: Module): ModuleSourceRootGroup = groupByBaseModules(listOf(module)).single() fun toModuleGroup(module: Module): ModuleSourceRootGroup = groupByBaseModules(listOf(module)).single()
} }
fun Module.toModuleGroup() = ModuleSourceRootMap(project).toModuleGroup(this)
private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String) private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String)
= externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId) = externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId)
@@ -18,30 +18,18 @@ package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.ex.ProjectRootManagerEx import com.intellij.openapi.roots.ex.ProjectRootManagerEx
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.xml.XmlFile
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator
import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
import org.jetbrains.kotlin.idea.maven.PomFile
import org.jetbrains.kotlin.idea.maven.changeCoroutineConfiguration
import org.jetbrains.kotlin.idea.maven.configuration.KotlinMavenConfigurator
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
sealed class ChangeCoroutineSupportFix( sealed class ChangeCoroutineSupportFix(
@@ -57,58 +45,7 @@ sealed class ChangeCoroutineSupportFix(
override fun invoke(project: Project, editor: Editor?, file: KtFile) { override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return
val runtimeUpdateRequired = coroutineSupportEnabled && findApplicableConfigurator(module).changeCoroutineConfiguration(module, coroutineSupport)
(getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false)
if (KotlinPluginUtil.isGradleModule(module) || KotlinPluginUtil.isMavenModule(module)) {
if (runtimeUpdateRequired) {
Messages.showErrorDialog(project,
"Coroutines support requires version 1.1 or later of the Kotlin runtime library. " +
"Please update the version in your build script.",
super.getText())
return
}
val element = if (KotlinPluginUtil.isGradleModule(module))
KotlinWithGradleConfigurator.changeCoroutineConfiguration(
module, CoroutineSupport.getCompilerArgument(coroutineSupport)
)
else
changeMavenCoroutineConfiguration(module, CoroutineSupport.getCompilerArgument(coroutineSupport))
if (element != null) {
OpenFileDescriptor(project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
}
return
}
if (runtimeUpdateRequired && !askUpdateRuntime(module, LanguageFeature.Coroutines.sinceApiVersion)) {
return
}
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(module)
ModuleRootModificationUtil.updateModel(module) {
facetSettings.coroutineSupport = coroutineSupport
facetSettings.apiLevel = LanguageVersion.KOTLIN_1_1
facetSettings.languageLevel = LanguageVersion.KOTLIN_1_1
}
}
private fun changeMavenCoroutineConfiguration(module: Module, value: String): PsiElement? {
fun doChangeMavenCoroutineConfiguration(): PsiElement? {
val psi = KotlinMavenConfigurator.findModulePomFile(module) as? XmlFile ?: return null
val pom = PomFile.forFileOrNull(psi) ?: return null
return pom.changeCoroutineConfiguration(value)
}
val element = doChangeMavenCoroutineConfiguration()
if (element == null) {
Messages.showErrorDialog(module.project,
"Failed to update.pom.xml. Please update the file manually.",
text)
}
return element
} }
} }
@@ -135,14 +72,18 @@ sealed class ChangeCoroutineSupportFix(
override fun getFamilyName() = "Enable/Disable coroutine support" override fun getFamilyName() = "Enable/Disable coroutine support"
override fun getText(): String { override fun getText(): String {
return when (coroutineSupport) { return getFixText(coroutineSupport)
LanguageFeature.State.ENABLED -> "Enable coroutine support"
LanguageFeature.State.ENABLED_WITH_WARNING -> "Enable coroutine support (with warning)"
LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> "Disable coroutine support"
}
} }
companion object : KotlinIntentionActionsFactory() { companion object : KotlinIntentionActionsFactory() {
fun getFixText(state: LanguageFeature.State): String {
return when (state) {
LanguageFeature.State.ENABLED -> "Enable coroutine support"
LanguageFeature.State.ENABLED_WITH_WARNING -> "Enable coroutine support (with warning)"
LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> "Disable coroutine support"
}
}
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> { override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val newCoroutineSupports = when (diagnostic.factory) { val newCoroutineSupports = when (diagnostic.factory) {
Errors.EXPERIMENTAL_FEATURE_ERROR -> { Errors.EXPERIMENTAL_FEATURE_ERROR -> {