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 changeLanguageFeatureConfiguration(feature: LanguageFeature, state: LanguageFeature.State): PsiElement?
fun changeLanguageFeatureConfiguration(feature: LanguageFeature, state: LanguageFeature.State, forTests: Boolean): PsiElement?
fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement?
@@ -153,10 +153,39 @@ class GroovyBuildScriptManipulator(
override fun changeLanguageFeatureConfiguration(
feature: LanguageFeature,
state: LanguageFeature.State
state: LanguageFeature.State,
forTests: Boolean
): PsiElement? {
// TODO: here we should use compileKotlin (TestKotlin).kotlinOptions.freeCompilerArgs = ["-XXLanguage:-InlineClasses"]
return null
val sign = when (state) {
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? =
@@ -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(
gradleFile: GroovyFile,
parameterName: String,
parameterValue: String,
forTests: Boolean
): PsiElement? {
val snippet = "$parameterName = \"$parameterValue\""
val kotlinBlock = gradleFile.getBlockOrCreate(if (forTests) "compileTestKotlin" else "compileKotlin")
for (stmt in kotlinBlock.statements) {
if ((stmt as? GrAssignmentExpression)?.lValue?.text == "kotlinOptions." + parameterName) {
return stmt.replaceWithStatementFromText("kotlinOptions." + snippet)
return addOrReplaceKotlinTaskParameter(
gradleFile, parameterName, "\"$parameterValue\"", forTests
) { insideKotlinOptions ->
if (insideKotlinOptions) {
replaceWithStatementFromText("kotlinOptions.$parameterName = \"$parameterValue\"")
} else {
replaceWithStatementFromText("$parameterName = \"$parameterValue\"")
}
}
kotlinBlock.getBlockOrCreate("kotlinOptions").apply {
addOrReplaceExpression(snippet) { stmt ->
(stmt as? GrAssignmentExpression)?.lValue?.text == parameterName
}
}
return kotlinBlock.parent
}
private fun getGroovyDependencySnippet(
@@ -111,7 +111,8 @@ class KotlinBuildScriptManipulator(
override fun changeLanguageFeatureConfiguration(
feature: LanguageFeature,
state: LanguageFeature.State
state: LanguageFeature.State,
forTests: Boolean
): PsiElement? =
scriptFile.changeLanguageFeatureConfiguration(feature, state)
@@ -256,7 +256,8 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
override fun changeGeneralFeatureConfiguration(
module: Module,
feature: LanguageFeature,
state: LanguageFeature.State
state: LanguageFeature.State,
forTests: Boolean
) {
val sinceVersion = feature.sinceApiVersion
@@ -270,9 +271,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return
}
val element = changeBuildGradle(module) {
getManipulator(it).changeLanguageFeatureConfiguration(feature, state)
}
val element = changeFeatureConfiguration(module, feature, state, forTests)
if (element != null) {
OpenFileDescriptor(module.project, element.containingFile.virtualFile, element.textRange.startOffset).navigate(true)
}
@@ -327,6 +326,15 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
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) =
changeBuildGradle(module) { 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.ExternalLibraryDescriptor
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.configuration.*
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
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><a href="update">Update</a> <a href="ignore">Ignore</a></p>
""".trimIndent().lines().joinToString(separator = ""),
createOutdatedBundledCompilerMessage(myProject, "1.0.0"))
createOutdatedBundledCompilerMessage(myProject, "1.0.0")
)
}
@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> {
val files = configureByFiles()
importProject()
@@ -56,7 +56,12 @@ interface KotlinProjectConfigurator {
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>)
@@ -348,7 +348,8 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro
override fun changeGeneralFeatureConfiguration(
module: Module,
feature: LanguageFeature,
state: LanguageFeature.State
state: LanguageFeature.State,
forTests: Boolean
) {
val sinceVersion = feature.sinceApiVersion
@@ -9,6 +9,7 @@ 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.openapi.roots.ModuleRootManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
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.findApplicableConfigurator
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.roots.invalidateProjectRoots
import org.jetbrains.kotlin.psi.KtFile
@@ -35,8 +37,9 @@ sealed class ChangeGeneralLanguageFeatureSupportFix(
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
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(
module: Module,
feature: LanguageFeature,
state: LanguageFeature.State
state: LanguageFeature.State,
forTests: Boolean
) {
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"]
}
}