Add enable / disable feature in Gradle (Groovy) project, forTests option

Part of KT-26775
This commit is contained in:
Mikhail Glukhikh
2018-09-18 11:12:59 +03:00
parent b194be7665
commit 43e4616fdf
17 changed files with 339 additions and 28 deletions
@@ -45,7 +45,7 @@ interface GradleBuildScriptManipulator<out Psi : PsiFile> {
fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? fun changeCoroutineConfiguration(coroutineOption: String): PsiElement?
fun changeLanguageFeatureConfiguration(feature: LanguageFeature, state: LanguageFeature.State): PsiElement? fun changeLanguageFeatureConfiguration(feature: LanguageFeature, state: LanguageFeature.State, forTests: Boolean): PsiElement?
fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement?
@@ -153,10 +153,39 @@ class GroovyBuildScriptManipulator(
override fun changeLanguageFeatureConfiguration( override fun changeLanguageFeatureConfiguration(
feature: LanguageFeature, feature: LanguageFeature,
state: LanguageFeature.State state: LanguageFeature.State,
forTests: Boolean
): PsiElement? { ): PsiElement? {
// TODO: here we should use compileKotlin (TestKotlin).kotlinOptions.freeCompilerArgs = ["-XXLanguage:-InlineClasses"] val sign = when (state) {
return null LanguageFeature.State.ENABLED -> "+"
LanguageFeature.State.DISABLED -> "-"
LanguageFeature.State.ENABLED_WITH_WARNING -> "+" // not supported normally
LanguageFeature.State.ENABLED_WITH_ERROR -> "-" // not supported normally
}
val languagePrefix = "-XXLanguage:"
val featureArgumentString = "$languagePrefix$sign${feature.name}"
val parameterName = "freeCompilerArgs"
return addOrReplaceKotlinTaskParameter(
scriptFile,
parameterName,
"[\"$featureArgumentString\"]",
forTests
) { insideKotlinOptions ->
val existingFeatureIndex = text.indexOf(feature.name)
val languagePrefixIndex = text.lastIndexOf(languagePrefix, existingFeatureIndex)
val newText = if (languagePrefixIndex != -1) {
text.substring(0, languagePrefixIndex) + featureArgumentString + text.substring(existingFeatureIndex + feature.name.length)
} else {
val splitText = text.split("]")
if (splitText.size != 2) {
val prefix = if (insideKotlinOptions) "kotlinOptions." else ""
"$prefix$parameterName = [\"$featureArgumentString\"]"
} else {
splitText[0] + ", \"$featureArgumentString\"]" + splitText[1]
}
}
replaceWithStatementFromText(newText)
}
} }
override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? = override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? =
@@ -233,28 +262,47 @@ class GroovyBuildScriptManipulator(
) )
} }
private fun addOrReplaceKotlinTaskParameter(
gradleFile: GroovyFile,
parameterName: String,
defaultValue: String,
forTests: Boolean,
replaceIt: GrStatement.(Boolean) -> GrStatement
): PsiElement? {
val kotlinBlock = gradleFile.getBlockOrCreate(if (forTests) "compileTestKotlin" else "compileKotlin")
for (stmt in kotlinBlock.statements) {
if ((stmt as? GrAssignmentExpression)?.lValue?.text == "kotlinOptions.$parameterName") {
return stmt.replaceIt(true)
}
}
kotlinBlock.getBlockOrCreate("kotlinOptions").apply {
statements.firstOrNull { stmt ->
(stmt as? GrAssignmentExpression)?.lValue?.text == parameterName
}?.let { stmt ->
stmt.replaceIt(false)
} ?: addLastExpressionInBlockIfNeeded("$parameterName = $defaultValue")
}
return kotlinBlock.parent
}
private fun changeKotlinTaskParameter( private fun changeKotlinTaskParameter(
gradleFile: GroovyFile, gradleFile: GroovyFile,
parameterName: String, parameterName: String,
parameterValue: String, parameterValue: String,
forTests: Boolean forTests: Boolean
): PsiElement? { ): PsiElement? {
val snippet = "$parameterName = \"$parameterValue\"" return addOrReplaceKotlinTaskParameter(
val kotlinBlock = gradleFile.getBlockOrCreate(if (forTests) "compileTestKotlin" else "compileKotlin") gradleFile, parameterName, "\"$parameterValue\"", forTests
) { insideKotlinOptions ->
for (stmt in kotlinBlock.statements) { if (insideKotlinOptions) {
if ((stmt as? GrAssignmentExpression)?.lValue?.text == "kotlinOptions." + parameterName) { replaceWithStatementFromText("kotlinOptions.$parameterName = \"$parameterValue\"")
return stmt.replaceWithStatementFromText("kotlinOptions." + snippet) } else {
replaceWithStatementFromText("$parameterName = \"$parameterValue\"")
} }
} }
kotlinBlock.getBlockOrCreate("kotlinOptions").apply {
addOrReplaceExpression(snippet) { stmt ->
(stmt as? GrAssignmentExpression)?.lValue?.text == parameterName
}
}
return kotlinBlock.parent
} }
private fun getGroovyDependencySnippet( private fun getGroovyDependencySnippet(
@@ -111,7 +111,8 @@ class KotlinBuildScriptManipulator(
override fun changeLanguageFeatureConfiguration( override fun changeLanguageFeatureConfiguration(
feature: LanguageFeature, feature: LanguageFeature,
state: LanguageFeature.State state: LanguageFeature.State,
forTests: Boolean
): PsiElement? = ): PsiElement? =
scriptFile.changeLanguageFeatureConfiguration(feature, state) scriptFile.changeLanguageFeatureConfiguration(feature, state)
@@ -256,7 +256,8 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
override fun changeGeneralFeatureConfiguration( override fun changeGeneralFeatureConfiguration(
module: Module, module: Module,
feature: LanguageFeature, feature: LanguageFeature,
state: LanguageFeature.State state: LanguageFeature.State,
forTests: Boolean
) { ) {
val sinceVersion = feature.sinceApiVersion val sinceVersion = feature.sinceApiVersion
@@ -270,9 +271,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return return
} }
val element = changeBuildGradle(module) { val element = changeFeatureConfiguration(module, feature, state, forTests)
getManipulator(it).changeLanguageFeatureConfiguration(feature, state)
}
if (element != null) { if (element != null) {
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true) OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
} }
@@ -327,6 +326,15 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
getManipulator(it).changeCoroutineConfiguration(coroutineOption) getManipulator(it).changeCoroutineConfiguration(coroutineOption)
} }
fun changeFeatureConfiguration(
module: Module,
feature: LanguageFeature,
state: LanguageFeature.State,
forTests: Boolean
) = changeBuildGradle(module) {
getManipulator(it).changeLanguageFeatureConfiguration(feature, state, forTests)
}
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) = fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String?, forTests: Boolean) =
changeBuildGradle(module) { buildScriptFile -> changeBuildGradle(module) { buildScriptFile ->
val manipulator = getManipulator(buildScriptFile) val manipulator = getManipulator(buildScriptFile)
@@ -12,6 +12,7 @@ import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.configuration.* import org.jetbrains.kotlin.idea.configuration.*
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
@@ -54,7 +55,8 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
<p>This may cause different set of errors and warnings reported in IDE.</p> <p>This may cause different set of errors and warnings reported in IDE.</p>
<p><a href="update">Update</a> <a href="ignore">Ignore</a></p> <p><a href="update">Update</a> <a href="ignore">Ignore</a></p>
""".trimIndent().lines().joinToString(separator = ""), """.trimIndent().lines().joinToString(separator = ""),
createOutdatedBundledCompilerMessage(myProject, "1.0.0")) createOutdatedBundledCompilerMessage(myProject, "1.0.0")
)
} }
@Test @Test
@@ -549,6 +551,66 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
} }
} }
@Test
fun testChangeFeatureSupport() {
val files = importProjectFromTestData()
runInEdtAndWait {
myTestFixture.project.executeWriteCommand("") {
KotlinWithGradleConfigurator.changeFeatureConfiguration(
myTestFixture.module, LanguageFeature.InlineClasses, LanguageFeature.State.ENABLED, false
)
}
checkFiles(files)
}
}
@Test
fun testDisableFeatureSupport() {
val files = importProjectFromTestData()
runInEdtAndWait {
myTestFixture.project.executeWriteCommand("") {
KotlinWithGradleConfigurator.changeFeatureConfiguration(
myTestFixture.module, LanguageFeature.InlineClasses, LanguageFeature.State.DISABLED, false
)
}
checkFiles(files)
}
}
@Test
fun testEnableFeatureSupport() {
val files = importProjectFromTestData()
runInEdtAndWait {
myTestFixture.project.executeWriteCommand("") {
KotlinWithGradleConfigurator.changeFeatureConfiguration(
myTestFixture.module, LanguageFeature.InlineClasses, LanguageFeature.State.ENABLED, false
)
}
checkFiles(files)
}
}
@Test
fun testEnableFeatureSupportToExistentArguments() {
val files = importProjectFromTestData()
runInEdtAndWait {
myTestFixture.project.executeWriteCommand("") {
KotlinWithGradleConfigurator.changeFeatureConfiguration(
myTestFixture.module, LanguageFeature.InlineClasses, LanguageFeature.State.ENABLED, false
)
}
checkFiles(files)
}
}
private fun importProjectFromTestData(): List<VirtualFile> { private fun importProjectFromTestData(): List<VirtualFile> {
val files = configureByFiles() val files = configureByFiles()
importProject() importProject()
@@ -56,7 +56,12 @@ interface KotlinProjectConfigurator {
fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State) fun changeCoroutineConfiguration(module: Module, state: LanguageFeature.State)
fun changeGeneralFeatureConfiguration(module: Module, feature: LanguageFeature, state: LanguageFeature.State) fun changeGeneralFeatureConfiguration(
module: Module,
feature: LanguageFeature,
state: LanguageFeature.State,
forTests: Boolean
)
fun addLibraryDependency(module: Module, element: PsiElement, library: ExternalLibraryDescriptor, libraryJarDescriptors: List<LibraryJarDescriptor>) fun addLibraryDependency(module: Module, element: PsiElement, library: ExternalLibraryDescriptor, libraryJarDescriptors: List<LibraryJarDescriptor>)
@@ -348,7 +348,8 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro
override fun changeGeneralFeatureConfiguration( override fun changeGeneralFeatureConfiguration(
module: Module, module: Module,
feature: LanguageFeature, feature: LanguageFeature,
state: LanguageFeature.State state: LanguageFeature.State,
forTests: Boolean
) { ) {
val sinceVersion = feature.sinceApiVersion val sinceVersion = feature.sinceApiVersion
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
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.ModuleRootManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
@@ -16,6 +17,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
import org.jetbrains.kotlin.idea.configuration.BuildSystemType import org.jetbrains.kotlin.idea.configuration.BuildSystemType
import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator import org.jetbrains.kotlin.idea.configuration.findApplicableConfigurator
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.roots.invalidateProjectRoots import org.jetbrains.kotlin.idea.roots.invalidateProjectRoots
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -35,8 +37,9 @@ sealed class ChangeGeneralLanguageFeatureSupportFix(
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 forTests = ModuleRootManager.getInstance(module).fileIndex.isInTestSourceContentKotlinAware(file.virtualFile)
findApplicableConfigurator(module).changeGeneralFeatureConfiguration(module, feature, featureSupport) findApplicableConfigurator(module).changeGeneralFeatureConfiguration(module, feature, featureSupport, forTests)
} }
} }
@@ -275,7 +275,8 @@ protected constructor(
override fun changeGeneralFeatureConfiguration( override fun changeGeneralFeatureConfiguration(
module: Module, module: Module,
feature: LanguageFeature, feature: LanguageFeature,
state: LanguageFeature.State state: LanguageFeature.State,
forTests: Boolean
) { ) {
val sinceVersion = feature.sinceApiVersion val sinceVersion = feature.sinceApiVersion
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:-InlineClasses"]
}
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:+InlineClasses"]
}
}
@@ -0,0 +1,19 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:-InlineClasses"]
}
}
@@ -0,0 +1,19 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:+InlineClasses"]
}
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:+SamConversionForKotlinFunctions"]
}
}
@@ -0,0 +1,24 @@
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0-rc-6"
}
}
apply plugin: "kotlin"
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-XXLanguage:+SamConversionForKotlinFunctions", "-XXLanguage:+InlineClasses"]
}
}