diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinVersionInfoProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinVersionInfoProvider.kt index 96fab5f8b65..15e11f6aa67 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinVersionInfoProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinVersionInfoProvider.kt @@ -62,25 +62,30 @@ fun getLibraryLanguageLevel( } fun getDefaultLanguageLevel( - module: Module, - explicitVersion: String? = null, - coerceRuntimeLibraryVersionToReleased: Boolean = true + module: Module, + explicitVersion: String? = null, + coerceRuntimeLibraryVersionToReleased: Boolean = true ): LanguageVersion { val libVersion = explicitVersion - ?: KotlinVersionInfoProvider.EP_NAME.extensions - .mapNotNull { it.getCompilerVersion(module) } - .addReleaseVersionIfNecessary(coerceRuntimeLibraryVersionToReleased) - .minWith(VersionComparatorUtil.COMPARATOR) - ?: return VersionView.RELEASED_VERSION - return when { - libVersion.startsWith("1.3") -> LanguageVersion.KOTLIN_1_3 - libVersion.startsWith("1.2") -> LanguageVersion.KOTLIN_1_2 - libVersion.startsWith("1.1") -> LanguageVersion.KOTLIN_1_1 - libVersion.startsWith("1.0") -> LanguageVersion.KOTLIN_1_0 - else -> VersionView.RELEASED_VERSION - } + ?: KotlinVersionInfoProvider.EP_NAME.extensions + .mapNotNull { it.getCompilerVersion(module) } + .addReleaseVersionIfNecessary(coerceRuntimeLibraryVersionToReleased) + .minWith(VersionComparatorUtil.COMPARATOR) + ?: return VersionView.RELEASED_VERSION + return libVersion.toLanguageVersion() } +fun String?.toLanguageVersion(): LanguageVersion = when { + this == null -> VersionView.RELEASED_VERSION + startsWith("1.3") -> LanguageVersion.KOTLIN_1_3 + startsWith("1.2") -> LanguageVersion.KOTLIN_1_2 + startsWith("1.1") -> LanguageVersion.KOTLIN_1_1 + startsWith("1.0") -> LanguageVersion.KOTLIN_1_0 + else -> VersionView.RELEASED_VERSION +} + +fun String?.toApiVersion(): ApiVersion = ApiVersion.createByLanguageVersion(toLanguageVersion()) + private fun Iterable.addReleaseVersionIfNecessary(shouldAdd: Boolean): Iterable = if (shouldAdd) this + VersionView.RELEASED_VERSION.versionString else this diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt index c5cdfe71e85..1e60bdcc89c 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GradleBuildScriptManipulator.kt @@ -22,10 +22,11 @@ import com.intellij.openapi.roots.ExternalLibraryDescriptor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.util.module import org.jetbrains.plugins.gradle.settings.GradleSettings -interface GradleBuildScriptManipulator { +interface GradleBuildScriptManipulator { val scriptFile: Psi val preferNewSyntax: Boolean @@ -44,6 +45,8 @@ interface GradleBuildScriptManipulator { fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? + fun changeLanguageFeatureConfiguration(feature: LanguageFeature, state: LanguageFeature.State): PsiElement? + fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? fun changeApiVersion(version: String, forTests: Boolean): PsiElement? diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt index 24ca07177f6..2a019017514 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/GroovyBuildScriptManipulator.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.roots.ExternalLibraryDescriptor import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import com.intellij.psi.codeStyle.CodeStyleManager +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.module @@ -150,6 +151,14 @@ class GroovyBuildScriptManipulator( return kotlinBlock.parent } + override fun changeLanguageFeatureConfiguration( + feature: LanguageFeature, + state: LanguageFeature.State + ): PsiElement? { + // TODO: here we should use compileKotlin (TestKotlin).kotlinOptions.freeCompilerArgs = ["-XXLanguage:-InlineClasses"] + return null + } + override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? = changeKotlinTaskParameter(scriptFile, "languageVersion", version, forTests) diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt index ad6def85ede..8c539ea732c 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinBuildScriptManipulator.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.roots.ExternalLibraryDescriptor import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS import org.jetbrains.kotlin.idea.util.application.runReadAction @@ -108,6 +109,12 @@ class KotlinBuildScriptManipulator( override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? = scriptFile.changeCoroutineConfiguration(coroutineOption) + override fun changeLanguageFeatureConfiguration( + feature: LanguageFeature, + state: LanguageFeature.State + ): PsiElement? = + scriptFile.changeLanguageFeatureConfiguration(feature, state) + override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? = scriptFile.changeKotlinTaskParameter("languageVersion", version, forTests) @@ -298,6 +305,14 @@ class KotlinBuildScriptManipulator( } } + private fun KtFile.changeLanguageFeatureConfiguration( + feature: LanguageFeature, + state: LanguageFeature.State + ): PsiElement? { + // TODO: Kotlin DSL + return null + } + private fun KtFile.changeKotlinTaskParameter(parameterName: String, parameterValue: String, forTests: Boolean): PsiElement? { val snippet = "$parameterName = \"$parameterValue\"" val taskName = if (forTests) "compileTestKotlin" else "compileKotlin" diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt index e84f6eba042..edca155d673 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.CoroutineSupport import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion +import org.jetbrains.kotlin.idea.facet.toApiVersion import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix import org.jetbrains.kotlin.idea.util.application.executeCommand @@ -234,7 +235,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) { val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED && - (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false) + getRuntimeLibraryVersion(module).toApiVersion() == ApiVersion.KOTLIN_1_0 if (runtimeUpdateRequired) { Messages.showErrorDialog( @@ -252,6 +253,31 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator { } } + override fun changeGeneralFeatureConfiguration( + module: Module, + feature: LanguageFeature, + state: LanguageFeature.State + ) { + val sinceVersion = feature.sinceApiVersion + + if (state != LanguageFeature.State.DISABLED && getRuntimeLibraryVersion(module).toApiVersion() < sinceVersion) { + Messages.showErrorDialog( + module.project, + "${feature.presentableName} support requires version $sinceVersion or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + ChangeCoroutineSupportFix.getFixText(state) + ) + return + } + + val element = changeBuildGradle(module) { + getManipulator(it).changeLanguageFeatureConfiguration(feature, state) + } + if (element != null) { + OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true) + } + } + override fun addLibraryDependency( module: Module, element: PsiElement, diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.kt index b09f295f6cd..18d663ae4a0 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.kt @@ -56,6 +56,8 @@ interface KotlinProjectConfigurator { fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) + fun changeGeneralFeatureConfiguration(module: Module, feature: LanguageFeature, state: LanguageFeature.State) + fun addLibraryDependency(module: Module, element: PsiElement, library: ExternalLibraryDescriptor, libraryJarDescriptors: List) companion object { diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt index d59f95726c4..f851b3bee6d 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt @@ -19,9 +19,8 @@ import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.psi.PsiElement import org.jetbrains.annotations.Contract import org.jetbrains.kotlin.config.* -import org.jetbrains.kotlin.idea.KotlinPluginUtil -import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion +import org.jetbrains.kotlin.idea.facet.toApiVersion import org.jetbrains.kotlin.idea.framework.ui.CreateLibraryDialogWithModules import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils import org.jetbrains.kotlin.idea.quickfix.askUpdateRuntime @@ -332,7 +331,7 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) { val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED && - (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false) + getRuntimeLibraryVersion(module).toApiVersion() == ApiVersion.KOTLIN_1_0 if (runtimeUpdateRequired && !askUpdateRuntime(module, LanguageFeature.Coroutines.sinceApiVersion)) { return @@ -346,6 +345,30 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro } } + override fun changeGeneralFeatureConfiguration( + module: Module, + feature: LanguageFeature, + state: LanguageFeature.State + ) { + val sinceVersion = feature.sinceApiVersion + + if (state != LanguageFeature.State.DISABLED && + getRuntimeLibraryVersion(module).toApiVersion() < sinceVersion && + !askUpdateRuntime(module, sinceVersion) + ) { + return + } + + val facetSettings = KotlinFacetSettingsProvider.getInstance(module.project).getInitializedSettings(module) + ModuleRootModificationUtil.updateModel(module) { + facetSettings.apiLevel = LanguageVersion.KOTLIN_1_3 + facetSettings.languageLevel = LanguageVersion.KOTLIN_1_3 + facetSettings.compilerSettings?.apply { + // TODO: JPS, module + } + } + } + override fun updateLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, requiredStdlibVersion: ApiVersion, forTests: Boolean) { val runtimeUpdateRequired = getRuntimeLibraryVersion(module)?.let { ApiVersion.parse(it) }?.let { runtimeVersion -> runtimeVersion < requiredStdlibVersion diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeGeneralLanguageFeatureSupportFix.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeGeneralLanguageFeatureSupportFix.kt new file mode 100644 index 00000000000..d7426c77cb2 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/ChangeGeneralLanguageFeatureSupportFix.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings +import org.jetbrains.kotlin.idea.configuration.BuildSystemType +import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator +import org.jetbrains.kotlin.idea.configuration.getBuildSystemType +import org.jetbrains.kotlin.idea.facet.KotlinFacet +import org.jetbrains.kotlin.idea.roots.invalidateProjectRoots +import org.jetbrains.kotlin.psi.KtFile + +sealed class ChangeGeneralLanguageFeatureSupportFix( + element: PsiElement, + feature: LanguageFeature, + featureSupport: LanguageFeature.State +) : AbstractChangeFeatureSupportLevelFix(element, feature, featureSupport, feature.presentableName) { + + class InModule( + element: PsiElement, + feature: LanguageFeature, + featureSupport: LanguageFeature.State + ) : ChangeGeneralLanguageFeatureSupportFix(element, feature, featureSupport) { + override fun getText() = "${super.getText()} in the current module" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return + + findApplicableConfigurator(module).changeGeneralFeatureConfiguration(module, feature, featureSupport) + } + } + + class InProject( + element: PsiElement, + feature: LanguageFeature, + featureSupport: LanguageFeature.State + ) : ChangeGeneralLanguageFeatureSupportFix(element, feature, featureSupport) { + override fun getText() = "${super.getText()} in the project" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + if (featureSupportEnabled) { + if (!checkUpdateRuntime(project, feature.sinceApiVersion)) return + } + KotlinCompilerSettings.getInstance(project).update { + // TODO: JPS, project + } + project.invalidateProjectRoots() + } + + } + + companion object : FeatureSupportIntentionActionsFactory() { + private val supportedFeatures = listOf(LanguageFeature.InlineClasses) + + fun getFixText(feature: LanguageFeature, state: LanguageFeature.State) = getFixText(state, feature.presentableName) + + override fun doCreateActions(diagnostic: Diagnostic): List { + val module = ModuleUtilCore.findModuleForPsiElement(diagnostic.psiElement) ?: return emptyList() + + fun shouldConfigureInProject(): Boolean { + val facetSettings = KotlinFacet.get(module)?.configuration?.settings + return (facetSettings == null || facetSettings.useProjectSettings) && + module.getBuildSystemType() == BuildSystemType.JPS + } + + return supportedFeatures.flatMap { feature -> + doCreateActions( + diagnostic, feature, allowWarningAndErrorMode = false, + quickFixConstructor = if (shouldConfigureInProject()) ::InProject else ::InModule + ) + } + } + } +} diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/JvmQuickFixRegistrar.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/JvmQuickFixRegistrar.kt index 02137fc932d..e5542d7b74a 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/JvmQuickFixRegistrar.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/quickfix/JvmQuickFixRegistrar.kt @@ -36,6 +36,9 @@ class JvmQuickFixRegistrar : QuickFixContributor { EXPERIMENTAL_FEATURE_ERROR.registerFactory(ChangeCoroutineSupportFix) EXPERIMENTAL_FEATURE_WARNING.registerFactory(ChangeCoroutineSupportFix) + EXPERIMENTAL_FEATURE_ERROR.registerFactory(ChangeGeneralLanguageFeatureSupportFix) + EXPERIMENTAL_FEATURE_WARNING.registerFactory(ChangeGeneralLanguageFeatureSupportFix) + NO_REFLECTION_IN_CLASS_PATH.registerFactory(AddReflectionQuickFix) MISSING_SCRIPT_BASE_CLASS.registerFactory(AddScriptRuntimeQuickFix) diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt index 518cb795f44..b1d6e55fe85 100644 --- a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt @@ -34,9 +34,11 @@ import org.jetbrains.kotlin.config.CoroutineSupport import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.configuration.* import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion +import org.jetbrains.kotlin.idea.facet.toApiVersion import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion import org.jetbrains.kotlin.idea.maven.* import org.jetbrains.kotlin.idea.quickfix.ChangeCoroutineSupportFix +import org.jetbrains.kotlin.idea.quickfix.ChangeGeneralLanguageFeatureSupportFix import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor @@ -249,7 +251,7 @@ protected constructor( override fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) { val runtimeUpdateRequired = state != LanguageFeature.State.DISABLED && - (getRuntimeLibraryVersion(module)?.startsWith("1.0") ?: false) + getRuntimeLibraryVersion(module).toApiVersion() == ApiVersion.KOTLIN_1_0 val messageTitle = ChangeCoroutineSupportFix.getFixText(state) if (runtimeUpdateRequired) { @@ -270,7 +272,31 @@ protected constructor( } - private fun changeMavenCoroutineConfiguration(module: Module, value: String, messageTitle: String): PsiElement? { + override fun changeGeneralFeatureConfiguration( + module: Module, + feature: LanguageFeature, + state: LanguageFeature.State + ) { + val sinceVersion = feature.sinceApiVersion + + val messageTitle = ChangeGeneralLanguageFeatureSupportFix.getFixText(feature, state) + if (state != LanguageFeature.State.DISABLED && getRuntimeLibraryVersion(module).toApiVersion() < sinceVersion) { + Messages.showErrorDialog( + module.project, + "${feature.presentableName} support requires version $sinceVersion or later of the Kotlin runtime library. " + + "Please update the version in your build script.", + messageTitle + ) + return + } + // TODO: here we should make something like https://kotlinlang.org/docs/reference/using-maven.html#specifying-compiler-options + } + + 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