Configuration: Use 'plugins' block in generated/modified Gradle scripts

#KT-20665 Fixed
This commit is contained in:
Alexey Sedunov
2018-05-11 18:52:55 +03:00
parent 909ff3a7ec
commit 1650e69980
12 changed files with 1392 additions and 311 deletions
@@ -32,17 +32,20 @@ class KotlinAndroidGradleModuleConfigurator internal constructor() : KotlinWithG
override val kotlinPluginName: String = KOTLIN_ANDROID
override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String =
if (forKotlinDsl) "kotlin(\"android\")" else "id 'org.jetbrains.kotlin.android' "
override fun addElementsToFile(file: PsiFile, isTopLevelProjectFile: Boolean, version: String): Boolean {
val manipulator = getManipulator(file)
val manipulator = getManipulator(file, false)
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
val jvmTarget = getJvmTarget(sdk, version)
return if (isTopLevelProjectFile) {
manipulator.configureProjectBuildScript(version)
manipulator.configureProjectBuildScript(kotlinPluginName, version)
}
else {
manipulator.configureModuleBuildScript(
kotlinPluginName,
getKotlinPluginExpression(file.isKtDsl()),
getStdlibArtifactName(sdk, version),
version,
jvmTarget
@@ -16,21 +16,31 @@
package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.roots.DependencyScope
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.idea.util.module
import org.jetbrains.plugins.gradle.settings.GradleSettings
interface GradleBuildScriptManipulator {
fun isConfigured(kotlinPluginName: String): Boolean
interface GradleBuildScriptManipulator<out Psi: PsiFile> {
val scriptFile: Psi
val preferNewSyntax: Boolean
fun isConfiguredWithOldSyntax(kotlinPluginName: String): Boolean
fun isConfigured(kotlinPluginExpression: String): Boolean
fun configureModuleBuildScript(
kotlinPluginName: String,
kotlinPluginExpression: String,
stdlibArtifactName: String,
version: String,
jvmTarget: String?
): Boolean
fun configureProjectBuildScript(version: String): Boolean
fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean
fun changeCoroutineConfiguration(coroutineOption: String): PsiElement?
@@ -45,4 +55,24 @@ interface GradleBuildScriptManipulator {
)
fun getKotlinStdlibVersion(): String?
// For settings.gradle/settings.gradle.kts
fun addMavenCentralPluginRepository()
fun addPluginRepository(repository: RepositoryDescription)
fun addResolutionStrategy(pluginId: String)
}
val MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX = GradleVersion.version("4.4")
fun GradleBuildScriptManipulator<*>.useNewSyntax(kotlinPluginName: String): Boolean {
if (!preferNewSyntax) return false
val module = scriptFile.module ?: return false
val path = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return false
val gradleVersion = GradleSettings.getInstance(module.project).getLinkedProjectSettings(path)?.resolveGradleVersion() ?: return false
if (gradleVersion < MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX) return false
return !isConfiguredWithOldSyntax(kotlinPluginName)
}
@@ -24,6 +24,7 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModifiableModelsProvider
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.versions.*
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
@@ -46,7 +47,7 @@ abstract class GradleKotlinFrameworkSupportProvider(
}
override fun createConfigurable(model: FrameworkSupportModel): FrameworkSupportInModuleConfigurable {
val configurable = super.createConfigurable(model)
val configurable = KotlinGradleFrameworkSupportInModuleConfigurable(model, this)
return object : FrameworkSupportInModuleConfigurable() {
override fun addSupport(module: Module, rootModel: ModifiableRootModel, modifiableModelsProvider: ModifiableModelsProvider) {
configurable.addSupport(module, rootModel, modifiableModelsProvider)
@@ -62,50 +63,75 @@ abstract class GradleKotlinFrameworkSupportProvider(
modifiableModelsProvider: ModifiableModelsProvider,
buildScriptData: BuildScriptDataBuilder
) {
addSupport(buildScriptData, rootModel.sdk)
addSupport(buildScriptData, module, rootModel.sdk, true)
}
open fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) {
open fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
var kotlinVersion = bundledRuntimeVersion()
val additionalRepository = getRepositoryForVersion(kotlinVersion)
if (isSnapshot(bundledRuntimeVersion())) {
kotlinVersion = LAST_SNAPSHOT_VERSION
}
if (additionalRepository != null) {
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository)
val useNewSyntax = buildScriptData.gradleVersion >= MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX
if (useNewSyntax) {
if (additionalRepository != null) {
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
updateSettingsScript(module) {
with(KotlinWithGradleConfigurator.getManipulator(it)) {
addPluginRepository(additionalRepository)
addMavenCentralPluginRepository()
}
}
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(oneLineRepository)
}
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(oneLineRepository)
buildScriptData.addPluginDefinitionInPluginsGroup(
getPluginExpression() + if (specifyPluginVersionIfNeeded) " version '$kotlinVersion'" else ""
)
} else {
if (additionalRepository != null) {
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository)
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(oneLineRepository)
}
buildScriptData
.addPluginDefinition(KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(getPluginId()))
.addBuildscriptRepositoriesDefinition("mavenCentral()")
.addRepositoriesDefinition("mavenCentral()")
.addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'")
}
buildScriptData
.addPluginDefinition(KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(getPluginId()))
.addBuildscriptRepositoriesDefinition("mavenCentral()")
.addRepositoriesDefinition("mavenCentral()")
.addBuildscriptPropertyDefinition("ext.kotlin_version = '$kotlinVersion'")
for (dependency in getDependencies(sdk)) {
buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile"))
buildScriptData.addDependencyNotation(KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "compile", !useNewSyntax))
}
for (dependency in getTestDependencies()) {
buildScriptData.addDependencyNotation(
if (":" in dependency)
"testCompile \"$dependency\""
else
KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "testCompile")
KotlinWithGradleConfigurator.getGroovyDependencySnippet(dependency, "testCompile", !useNewSyntax)
)
}
buildScriptData.addBuildscriptDependencyNotation(KotlinWithGradleConfigurator.CLASSPATH)
if (useNewSyntax) {
updateSettingsScript(module) { updateSettingsScript(it, specifyPluginVersionIfNeeded) }
}
}
protected open fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) { }
protected abstract fun getDependencies(sdk: Sdk?): List<String>
protected open fun getTestDependencies(): List<String> = listOf()
protected abstract fun getPluginId(): String
protected abstract fun getPluginExpression(): String
protected abstract fun getDescription(): String
}
@@ -116,11 +142,12 @@ open class GradleKotlinJavaFrameworkSupportProvider(
) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.SMALL_LOGO) {
override fun getPluginId() = KotlinGradleModuleConfigurator.KOTLIN
override fun getPluginExpression() = "id 'org.jetbrains.kotlin.jvm'"
override fun getDependencies(sdk: Sdk?) = listOf(getStdlibArtifactId(sdk, bundledRuntimeVersion()))
override fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) {
super.addSupport(buildScriptData, sdk)
override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded)
val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion())
if (jvmTarget != null) {
val description = jvmTarget.description
@@ -138,21 +165,35 @@ open class GradleKotlinJSFrameworkSupportProvider(
) : GradleKotlinFrameworkSupportProvider(frameworkTypeId, displayName, KotlinIcons.JS) {
override fun getPluginId() = KotlinJsGradleModuleConfigurator.KOTLIN_JS
override fun getPluginExpression() = "id 'kotlin2js'"
override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_JS_STDLIB_ID)
override fun getTestDependencies() = listOf(MAVEN_JS_TEST_ID)
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
if (specifyPluginVersionIfNeeded) {
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin2js")
}
}
override fun getDescription() = "A Kotlin library or application targeting JavaScript"
}
open class GradleKotlinMPPCommonFrameworkSupportProvider :
GradleKotlinFrameworkSupportProvider("KOTLIN_MPP_COMMON", "Kotlin (Multiplatform Common - Experimental)", KotlinIcons.MPP) {
override fun getPluginId() = "kotlin-platform-common"
override fun getPluginExpression() = "id 'kotlin-platform-common'"
override fun getDependencies(sdk: Sdk?) = listOf(MAVEN_COMMON_STDLIB_ID)
override fun getTestDependencies() = listOf(MAVEN_COMMON_TEST_ID, MAVEN_COMMON_TEST_ANNOTATIONS_ID)
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
if (specifyPluginVersionIfNeeded) {
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-common")
}
}
override fun getDescription() = "Shared code for a Kotlin multiplatform project (targeting JVM and JS)"
}
@@ -160,11 +201,13 @@ class GradleKotlinMPPJavaFrameworkSupportProvider
: GradleKotlinJavaFrameworkSupportProvider("KOTLIN_MPP_JVM", "Kotlin (Multiplatform JVM - Experimental)") {
override fun getPluginId() = "kotlin-platform-jvm"
override fun getPluginExpression() = "id 'kotlin-platform-jvm'"
override fun getDescription() = "JVM-specific code for a Kotlin multiplatform project"
override fun getTestDependencies() = listOf(MAVEN_TEST_ID, MAVEN_TEST_JUNIT_ID, "junit:junit:4.12")
override fun addSupport(buildScriptData: BuildScriptDataBuilder, sdk: Sdk?) {
super.addSupport(buildScriptData, sdk)
override fun addSupport(buildScriptData: BuildScriptDataBuilder, module: Module, sdk: Sdk?, specifyPluginVersionIfNeeded: Boolean) {
super.addSupport(buildScriptData, module, sdk, specifyPluginVersionIfNeeded)
val jvmTarget = getDefaultJvmTarget(sdk, bundledRuntimeVersion())
if (jvmTarget != null) {
val description = jvmTarget.description
@@ -172,11 +215,24 @@ class GradleKotlinMPPJavaFrameworkSupportProvider
}
}
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
if (specifyPluginVersionIfNeeded) {
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-jvm")
}
}
}
class GradleKotlinMPPJSFrameworkSupportProvider
: GradleKotlinJSFrameworkSupportProvider("KOTLIN_MPP_JS", "Kotlin (Multiplatform JS - Experimental)") {
override fun getPluginId() = "kotlin-platform-js"
override fun getPluginExpression() = "id 'kotlin-platform-js'"
override fun updateSettingsScript(settingsScript: PsiFile, specifyPluginVersionIfNeeded: Boolean) {
if (specifyPluginVersionIfNeeded) {
KotlinWithGradleConfigurator.getManipulator(settingsScript).addResolutionStrategy("kotlin-platform-js")
}
}
override fun getDescription() = "JavaScript-specific code for a Kotlin multiplatform project"
}
@@ -18,71 +18,109 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.roots.DependencyScope
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.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
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.GrAssignmentExpression
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 org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement
class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : GradleBuildScriptManipulator {
override fun isConfigured(kotlinPluginName: String): Boolean {
val fileText = runReadAction { groovyScript.text }
class GroovyBuildScriptManipulator(
override val scriptFile: GroovyFile,
override val preferNewSyntax: Boolean
) : GradleBuildScriptManipulator<GroovyFile> {
override fun isConfiguredWithOldSyntax(kotlinPluginName: String): Boolean {
val fileText = runReadAction { scriptFile.text }
return containsDirective(fileText, getApplyPluginDirective(kotlinPluginName)) &&
fileText.contains("org.jetbrains.kotlin") &&
fileText.contains("kotlin-stdlib")
}
override fun isConfigured(kotlinPluginExpression: String): Boolean {
val fileText = runReadAction { scriptFile.text }
val pluginsBlockText = runReadAction { scriptFile.getBlockByName("plugins")?.text ?: "" }
return (containsDirective(pluginsBlockText, kotlinPluginExpression)) &&
fileText.contains("org.jetbrains.kotlin") &&
fileText.contains("kotlin-stdlib")
}
override fun configureModuleBuildScript(
kotlinPluginName: String,
kotlinPluginExpression: String,
stdlibArtifactName: String,
version: String,
jvmTarget: String?
): Boolean {
val oldText = groovyScript.text
val applyPluginDirective = getGroovyApplyPluginDirective(kotlinPluginName)
if (!containsDirective(groovyScript.text, applyPluginDirective)) {
val apply = GroovyPsiElementFactory.getInstance(groovyScript.project).createExpressionFromText(applyPluginDirective)
val applyStatement = getApplyStatement(groovyScript)
if (applyStatement != null) {
groovyScript.addAfter(apply, applyStatement)
} else {
val anchorBlock = groovyScript.getBlockByName("plugins") ?: groovyScript.getBlockByName("buildscript")
if (anchorBlock != null) {
groovyScript.addAfter(apply, anchorBlock.parent)
} else {
groovyScript.addAfter(apply, groovyScript.statements.lastOrNull() ?: groovyScript.firstChild)
val oldText = scriptFile.text
val useNewSyntax = useNewSyntax(kotlinPluginName)
if (useNewSyntax) {
scriptFile
.getPluginsBlock()
.addLastExpressionInBlockIfNeeded("$kotlinPluginExpression version '$version'")
scriptFile.getRepositoriesBlock().apply {
val repository = getRepositoryForVersion(version)
if (repository != null) {
scriptFile.module?.getBuildScriptSettingsPsiFile()?.let {
with(KotlinWithGradleConfigurator.getManipulator(it)) {
addPluginRepository(repository)
addMavenCentralPluginRepository()
}
}
}
addMavenCentralIfMissing()
}
}
else {
val applyPluginDirective = getGroovyApplyPluginDirective(kotlinPluginName)
if (!containsDirective(scriptFile.text, applyPluginDirective)) {
val apply = GroovyPsiElementFactory.getInstance(scriptFile.project).createExpressionFromText(applyPluginDirective)
val applyStatement = getApplyStatement(scriptFile)
if (applyStatement != null) {
scriptFile.addAfter(apply, applyStatement)
} else {
val anchorBlock = scriptFile.getBlockByName("plugins") ?: scriptFile.getBlockByName("buildscript")
if (anchorBlock != null) {
scriptFile.addAfter(apply, anchorBlock.parent)
} else {
scriptFile.addAfter(apply, scriptFile.statements.lastOrNull() ?: scriptFile.firstChild)
}
}
}
scriptFile.getRepositoriesBlock().apply {
addRepository(version)
addMavenCentralIfMissing()
}
}
groovyScript.getRepositoriesBlock().apply {
addRepository(version)
addMavenCentralIfMissing()
}
groovyScript.getDependenciesBlock().apply {
addExpressionInBlockIfNeeded(getGroovyDependencySnippet(stdlibArtifactName), false)
scriptFile.getDependenciesBlock().apply {
addExpressionOrStatementInBlockIfNeeded(getGroovyDependencySnippet(stdlibArtifactName, !useNewSyntax), false, false)
}
if (jvmTarget != null) {
changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = false)
changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = true)
changeKotlinTaskParameter(scriptFile, "jvmTarget", jvmTarget, forTests = false)
changeKotlinTaskParameter(scriptFile, "jvmTarget", jvmTarget, forTests = true)
}
return groovyScript.text != oldText
return scriptFile.text != oldText
}
override fun configureProjectBuildScript(version: String): Boolean {
val oldText = groovyScript.text
groovyScript.apply {
override fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean {
if (useNewSyntax(kotlinPluginName)) return false
val oldText = scriptFile.text
scriptFile.apply {
getBuildScriptBlock().apply {
addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version))
}
@@ -97,12 +135,12 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
}
}
return oldText != groovyScript.text
return oldText != scriptFile.text
}
override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? {
val snippet = "coroutines \"$coroutineOption\""
val kotlinBlock = groovyScript.getBlockOrCreate("kotlin")
val kotlinBlock = scriptFile.getBlockOrCreate("kotlin")
kotlinBlock.getBlockOrCreate("experimental").apply {
addOrReplaceExpression(snippet) { stmt ->
(stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines"
@@ -113,10 +151,10 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
}
override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? =
changeKotlinTaskParameter(groovyScript, "languageVersion", version, forTests)
changeKotlinTaskParameter(scriptFile, "languageVersion", version, forTests)
override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? =
changeKotlinTaskParameter(groovyScript, "apiVersion", version, forTests)
changeKotlinTaskParameter(scriptFile, "apiVersion", version, forTests)
override fun addKotlinLibraryToModuleBuildScript(
scope: DependencyScope,
@@ -131,20 +169,20 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
libraryDescriptor.maxVersion
)
groovyScript.getDependenciesBlock().apply {
scriptFile.getDependenciesBlock().apply {
addLastExpressionInBlockIfNeeded(dependencyString)
}
}
override fun getKotlinStdlibVersion(): String? {
val versionProperty = "\$kotlin_version"
groovyScript.getBlockByName("buildScript")?.let {
scriptFile.getBlockByName("buildScript")?.let {
if (it.text.contains("ext.kotlin_version = ")) {
return versionProperty
}
}
val dependencies = groovyScript.getBlockByName("dependencies")?.statements
val dependencies = scriptFile.getBlockByName("dependencies")?.statements
val stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:"
dependencies?.forEach { dependency ->
val dependencyText = dependency.text
@@ -158,6 +196,35 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
return null
}
private fun addPluginRepositoryExpression(expression: String) {
scriptFile
.getBlockOrPrepend("pluginManagement")
.getBlockOrCreate("repositories")
.addLastExpressionInBlockIfNeeded(expression)
}
override fun addMavenCentralPluginRepository() {
addPluginRepositoryExpression("mavenCentral()")
}
override fun addPluginRepository(repository: RepositoryDescription) {
addPluginRepositoryExpression(repository.toGroovyRepositorySnippet())
}
override fun addResolutionStrategy(pluginId: String) {
scriptFile
.getBlockOrPrepend("pluginManagement")
.getBlockOrCreate("resolutionStrategy")
.getBlockOrCreate("eachPlugin")
.addLastStatementInBlockIfNeeded(
"""
if (requested.id.id == "$pluginId") {
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}")
}
""".trimIndent()
)
}
private fun changeKotlinTaskParameter(
gradleFile: GroovyFile,
parameterName: String,
@@ -182,7 +249,10 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
return kotlinBlock.parent
}
private fun getGroovyDependencySnippet(artifactName: String) = "compile \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
private fun getGroovyDependencySnippet(
artifactName: String,
withVersion: Boolean
) = "compile \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\""
private fun getApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
@@ -195,13 +265,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
private fun getApplyStatement(file: GroovyFile): GrApplicationStatement? =
file.getChildrenOfType<GrApplicationStatement>().find { it.invokedExpression.text == "apply" }
private fun PsiElement.getBlockByName(name: String): GrClosableBlock? {
return getChildrenOfType<GrMethodCallExpression>()
.filter { it.closureArguments.isNotEmpty() }
.find { it.invokedExpression.text == name }
?.let { it.closureArguments[0] }
}
private fun GrClosableBlock.addRepository(version: String): Boolean {
val repository = getRepositoryForVersion(version)
val snippet = when {
@@ -212,13 +275,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
return addLastExpressionInBlockIfNeeded(snippet)
}
private fun GrClosableBlock.addMavenCentralIfMissing(): Boolean =
if (!isRepositoryConfigured(text)) addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL) else false
private fun GrStatementOwner.getRepositoriesBlock() = getBlockOrCreate("repositories")
private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies")
private fun GrStatementOwner.getBuildScriptBlock() = getBlockOrCreate("buildscript") { newBlock ->
val pluginsBlock = getBlockByName("plugins") ?: return@getBlockOrCreate false
addBefore(newBlock, pluginsBlock.parent)
@@ -231,21 +287,12 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
private fun GrStatementOwner.getBuildScriptDependenciesBlock(): GrClosableBlock =
getBuildScriptBlock().getBlockOrCreate("dependencies")
private fun GrStatementOwner.getBlockOrCreate(
name: String,
customInsert: GrStatementOwner.(newBlock: PsiElement) -> Boolean = { false }
): GrClosableBlock {
var block = getBlockByName(name)
if (block == null) {
val factory = GroovyPsiElementFactory.getInstance(project)
val newBlock = factory.createExpressionFromText("$name{\n}\n")
if (!customInsert(newBlock)) {
addAfter(newBlock, statements.lastOrNull() ?: firstChild)
}
block = getBlockByName(name)!!
}
return block
}
private fun GrClosableBlock.addMavenCentralIfMissing(): Boolean =
if (!isRepositoryConfigured(text)) addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL) else false
private fun GrStatementOwner.getRepositoriesBlock() = getBlockOrCreate("repositories")
private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies")
private fun GrClosableBlock.addOrReplaceExpression(snippet: String, predicate: (GrStatement) -> Boolean) {
statements.firstOrNull(predicate)?.let { stmt ->
@@ -255,29 +302,6 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
addLastExpressionInBlockIfNeeded(snippet)
}
private fun GrClosableBlock.addLastExpressionInBlockIfNeeded(expressionText: String): Boolean =
addExpressionInBlockIfNeeded(expressionText, false)
private fun GrClosableBlock.addFirstExpressionInBlockIfNeeded(expressionText: String): Boolean =
addExpressionInBlockIfNeeded(expressionText, true)
private fun GrClosableBlock.addExpressionInBlockIfNeeded(expressionText: String, isFirst: Boolean): Boolean {
if (text.contains(expressionText)) return false
val newStatement = GroovyPsiElementFactory.getInstance(project).createExpressionFromText(expressionText)
CodeStyleManager.getInstance(project).reformat(newStatement)
if (!isFirst && statements.isNotEmpty()) {
val lastStatement = statements[statements.size - 1]
if (lastStatement != null) {
addAfter(newStatement, lastStatement)
}
} else {
if (firstChild != null) {
addAfter(newStatement, firstChild)
}
}
return true
}
private fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
private fun GrStatement.replaceWithStatementFromText(snippet: String): GrStatement {
@@ -291,5 +315,65 @@ class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : Gradl
private val VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE)
private val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
private val CLASSPATH = "classpath \"$KOTLIN_GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
private fun PsiElement.getBlockByName(name: String): GrClosableBlock? {
return getChildrenOfType<GrMethodCallExpression>()
.filter { it.closureArguments.isNotEmpty() }
.find { it.invokedExpression.text == name }
?.let { it.closureArguments[0] }
}
fun GrStatementOwner.getBlockOrCreate(
name: String,
customInsert: GrStatementOwner.(newBlock: PsiElement) -> Boolean = { false }
): GrClosableBlock {
var block = getBlockByName(name)
if (block == null) {
val factory = GroovyPsiElementFactory.getInstance(project)
val newBlock = factory.createExpressionFromText("$name{\n}\n")
if (!customInsert(newBlock)) {
addAfter(newBlock, statements.lastOrNull() ?: firstChild)
}
block = getBlockByName(name)!!
}
return block
}
fun GrStatementOwner.getBlockOrPrepend(name: String) = getBlockOrCreate(name) { newBlock ->
addAfter(newBlock, null)
true
}
fun GrStatementOwner.getPluginsBlock() = getBlockOrCreate("plugins") { newBlock ->
addAfter(newBlock, getBlockByName("buildscript"))
true
}
fun GrClosableBlock.addLastExpressionInBlockIfNeeded(expressionText: String): Boolean =
addExpressionOrStatementInBlockIfNeeded(expressionText, false, false)
fun GrClosableBlock.addLastStatementInBlockIfNeeded(expressionText: String): Boolean =
addExpressionOrStatementInBlockIfNeeded(expressionText, true, false)
private fun GrClosableBlock.addFirstExpressionInBlockIfNeeded(expressionText: String): Boolean =
addExpressionOrStatementInBlockIfNeeded(expressionText, false, true)
private fun GrClosableBlock.addExpressionOrStatementInBlockIfNeeded(text: String, isStatement: Boolean, isFirst: Boolean): Boolean {
if (statements.any { StringUtil.equalsIgnoreWhitespaces(it.text, text) }) return false
val psiFactory = GroovyPsiElementFactory.getInstance(project)
val newStatement = if (isStatement) psiFactory.createStatementFromText(text) else psiFactory.createExpressionFromText(text)
CodeStyleManager.getInstance(project).reformat(newStatement)
if (!isFirst && statements.isNotEmpty()) {
val lastStatement = statements[statements.size - 1]
if (lastStatement != null) {
addAfter(newStatement, lastStatement)
}
} else {
if (firstChild != null) {
addAfter(newStatement, firstChild)
}
}
return true
}
}
}
@@ -18,24 +18,35 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.roots.DependencyScope
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.idea.configuration.KotlinWithGradleConfigurator.Companion.getBuildScriptSettingsPsiFile
import org.jetbrains.kotlin.idea.inspections.gradle.GradleHeuristicHelper.PRODUCTION_DEPENDENCY_STATEMENTS
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.utils.addToStdlib.cast
class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBuildScriptManipulator {
override fun isConfigured(kotlinPluginName: String): Boolean =
runReadAction {
kotlinScript.containsApplyKotlinPlugin(kotlinPluginName) && kotlinScript.containsCompileStdLib()
}
class KotlinBuildScriptManipulator(
override val scriptFile: KtFile,
override val preferNewSyntax: Boolean
) : GradleBuildScriptManipulator<KtFile> {
override fun isConfiguredWithOldSyntax(kotlinPluginName: String) = runReadAction {
scriptFile.containsApplyKotlinPlugin(kotlinPluginName) && scriptFile.containsCompileStdLib()
}
override fun configureProjectBuildScript(version: String): Boolean {
val originalText = kotlinScript.text
kotlinScript.getBuildScriptBlock()?.apply {
override fun isConfigured(kotlinPluginExpression: String): Boolean = runReadAction {
scriptFile.containsKotlinPluginInPluginsGroup(kotlinPluginExpression) && scriptFile.containsCompileStdLib()
}
override fun configureProjectBuildScript(kotlinPluginName: String, version: String): Boolean {
if (useNewSyntax(kotlinPluginName)) return false
val originalText = scriptFile.text
scriptFile.getBuildScriptBlock()?.apply {
addDeclarationIfMissing("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true).also {
addExpressionAfterIfMissing("$GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$version\"", it)
}
@@ -48,50 +59,70 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
getDependenciesBlock()?.addPluginToClassPathIfMissing()
}
return originalText != kotlinScript.text
return originalText != scriptFile.text
}
override fun configureModuleBuildScript(
kotlinPluginName: String,
kotlinPluginExpression: String,
stdlibArtifactName: String,
version: String,
jvmTarget: String?
): Boolean {
val originalText = kotlinScript.text
kotlinScript.apply {
script?.blockExpression?.addDeclarationIfMissing("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true)
getApplyBlock()?.createPluginIfMissing(kotlinPluginName)
getDependenciesBlock()?.addCompileStdlibIfMissing(stdlibArtifactName)
getRepositoriesBlock()?.apply {
addRepositoryIfMissing(version)
addMavenCentralIfMissing()
val originalText = scriptFile.text
val useNewSyntax = useNewSyntax(kotlinPluginName)
scriptFile.apply {
if (useNewSyntax) {
createPluginInPluginsGroupIfMissing(kotlinPluginExpression, version)
getDependenciesBlock()?.addNoVersionCompileStdlibIfMissing(stdlibArtifactName)
getRepositoriesBlock()?.apply {
val repository = getRepositoryForVersion(version)
if (repository != null) {
scriptFile.module?.getBuildScriptSettingsPsiFile()?.let {
with(KotlinWithGradleConfigurator.getManipulator(it)) {
addPluginRepository(repository)
addMavenCentralPluginRepository()
}
}
}
addMavenCentralIfMissing()
}
} else {
script?.blockExpression?.addDeclarationIfMissing("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true)
getApplyBlock()?.createPluginIfMissing(kotlinPluginName)
getDependenciesBlock()?.addCompileStdlibIfMissing(stdlibArtifactName)
getRepositoriesBlock()?.apply {
addRepositoryIfMissing(version)
addMavenCentralIfMissing()
}
}
jvmTarget?.let {
changeKotlinTaskParameter("jvmTarget", it, forTests = false)
changeKotlinTaskParameter("jvmTarget", it, forTests = true)
}
}
return originalText != kotlinScript.text
return originalText != scriptFile.text
}
override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? =
kotlinScript.changeCoroutineConfiguration(coroutineOption)
scriptFile.changeCoroutineConfiguration(coroutineOption)
override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? =
kotlinScript.changeKotlinTaskParameter("languageVersion", version, forTests)
scriptFile.changeKotlinTaskParameter("languageVersion", version, forTests)
override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? =
kotlinScript.changeKotlinTaskParameter("apiVersion", version, forTests)
scriptFile.changeKotlinTaskParameter("apiVersion", version, forTests)
override fun addKotlinLibraryToModuleBuildScript(
scope: DependencyScope,
libraryDescriptor: ExternalLibraryDescriptor,
isAndroidModule: Boolean
) {
val kotlinLibraryVersion = libraryDescriptor.maxVersion.takeIf { it != GSK_KOTLIN_VERSION_PROPERTY_NAME }
kotlinScript.getDependenciesBlock()?.apply {
val kotlinLibraryVersion = libraryDescriptor.maxVersion.let {
if (it == GSK_KOTLIN_VERSION_PROPERTY_NAME) "\$$it" else it
}
scriptFile.getDependenciesBlock()?.apply {
addExpressionIfMissing(
getCompileDependencySnippet(
libraryDescriptor.libraryGroupId,
@@ -103,10 +134,46 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
}
}
override fun getKotlinStdlibVersion(): String? = kotlinScript.getKotlinStdlibVersion()
override fun getKotlinStdlibVersion(): String? = scriptFile.getKotlinStdlibVersion()
private fun KtBlockExpression.addCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? =
findStdLibDependency() ?: addExpressionIfMissing(getCompileDependencySnippet(KOTLIN_GROUP_ID, stdlibArtifactName)) as? KtCallExpression
findStdLibDependency()
?: addExpressionIfMissing(
getCompileDependencySnippet(
KOTLIN_GROUP_ID,
stdlibArtifactName,
version = "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME"
)
) as? KtCallExpression
private fun addPluginRepositoryExpression(expression: String) {
scriptFile.getPluginManagementBlock()?.findOrCreateBlock("repositories")?.addExpressionIfMissing(expression)
}
override fun addMavenCentralPluginRepository() {
addPluginRepositoryExpression("mavenCentral()")
}
override fun addPluginRepository(repository: RepositoryDescription) {
addPluginRepositoryExpression(repository.toKotlinRepositorySnippet())
}
override fun addResolutionStrategy(pluginId: String) {
scriptFile
.getPluginManagementBlock()
?.findOrCreateBlock("resolutionStrategy")
?.findOrCreateBlock("eachPlugin")
?.addExpressionIfMissing(
"""
if (requested.id.id == "$pluginId") {
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}{requested.version}")
}
""".trimIndent()
)
}
private fun KtBlockExpression.addNoVersionCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? =
findStdLibDependency() ?: addExpressionIfMissing("compile(${getKotlinModuleDependencySnippet(stdlibArtifactName, null)})") as? KtCallExpression
private fun KtFile.containsCompileStdLib(): Boolean =
findScriptInitializer("dependencies")?.getBlock()?.findStdLibDependency() != null
@@ -114,12 +181,39 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
private fun KtFile.containsApplyKotlinPlugin(pluginName: String): Boolean =
findScriptInitializer("apply")?.getBlock()?.findPlugin(pluginName) != null
private fun KtFile.containsKotlinPluginInPluginsGroup(pluginName: String): Boolean =
findScriptInitializer("plugins")?.getBlock()?.findPluginInPluginsGroup(pluginName) != null
private fun KtBlockExpression.findPlugin(pluginName: String): KtCallExpression? {
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
it.calleeExpression?.text == "plugin" && it.valueArguments.firstOrNull()?.text == "\"$pluginName\""
}
}
private fun KtBlockExpression.findPluginInPluginsGroup(pluginName: String): KtCallExpression? {
return PsiTreeUtil.getChildrenOfAnyType(
this,
KtCallExpression::class.java,
KtBinaryExpression::class.java,
KtDotQualifiedExpression::class.java
).mapNotNull {
when (it) {
is KtCallExpression -> it
is KtBinaryExpression -> {
if (it.operationReference.text == "version") it.left as? KtCallExpression else null
}
is KtDotQualifiedExpression -> {
if ((it.selectorExpression as? KtCallExpression)?.calleeExpression?.text == "version") {
it.receiverExpression as? KtCallExpression
} else null
}
else -> null
}
}.find {
"${it.calleeExpression?.text?.trim() ?: ""}(${it.valueArguments.firstOrNull()?.text ?: ""})" == pluginName
}
}
private fun KtFile.findScriptInitializer(startsWith: String): KtScriptInitializer? =
PsiTreeUtil.findChildrenOfType(this, KtScriptInitializer::class.java).find { it.text.startsWith(startsWith) }
@@ -140,7 +234,7 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
return findScriptInitializer("dependencies")?.getBlock()?.let {
val expression = it.findStdLibDependency()?.valueArguments?.firstOrNull()?.getArgumentExpression()
when (expression) {
is KtCallExpression -> expression.valueArguments[1].text.trim('\"')
is KtCallExpression -> expression.valueArguments.getOrNull(1)?.text?.trim('\"')
is KtStringTemplateExpression -> expression.text?.trim('\"')?.substringAfterLast(":")?.removePrefix("$")
else -> null
}
@@ -150,23 +244,39 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
private fun KtBlockExpression.findStdLibDependency(): KtCallExpression? {
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
val calleeText = it.calleeExpression?.text
calleeText in PRODUCTION_DEPENDENCY_STATEMENTS && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() ?: false)
calleeText in PRODUCTION_DEPENDENCY_STATEMENTS && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib()
?: false)
}
}
private fun KtExpression.isKotlinStdLib(): Boolean = when (this) {
is KtCallExpression -> calleeExpression?.text == "kotlinModule" &&
valueArguments.firstOrNull()?.getArgumentExpression()?.text?.startsWith("\"stdlib") ?: false
is KtCallExpression -> {
val calleeText = calleeExpression?.text
(calleeText == "kotlinModule" || calleeText == "kotlin") &&
valueArguments.firstOrNull()?.getArgumentExpression()?.text?.startsWith("\"stdlib") ?: false
}
is KtStringTemplateExpression -> text.startsWith("\"$STDLIB_ARTIFACT_PREFIX")
else -> false
}
private fun KtFile.getPluginManagementBlock(): KtBlockExpression? =
findScriptInitializer("pluginManagement")?.getBlock() ?: addTopLevelBlock("pluginManagement", true)
private fun KtFile.getRepositoriesBlock(): KtBlockExpression? =
findScriptInitializer("repositories")?.getBlock() ?: addTopLevelBlock("repositories")
private fun KtFile.getDependenciesBlock(): KtBlockExpression? =
findScriptInitializer("dependencies")?.getBlock() ?: addTopLevelBlock("dependencies")
private fun KtFile.getPluginsBlock(): KtBlockExpression? =
findScriptInitializer("plugins")?.getBlock() ?: addTopLevelBlock("plugins", true)
private fun KtFile.createPluginInPluginsGroupIfMissing(pluginName: String, version: String): KtCallExpression? =
getPluginsBlock()?.let {
it.findPluginInPluginsGroup(pluginName)
?: it.addExpressionIfMissing("$pluginName version \"$version\"") as? KtCallExpression
}
private fun KtFile.createApplyBlock(): KtBlockExpression? {
val apply = psiFactory.createScriptInitializer("apply {\n}")
val plugins = findScriptInitializer("plugins")
@@ -212,6 +322,15 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
}
}
private fun KtBlockExpression.getRepositorySnippet(version: String): String? {
val repository = getRepositoryForVersion(version)
return when {
repository != null -> repository.toKotlinRepositorySnippet()
!isRepositoryConfigured(text) -> MAVEN_CENTRAL
else -> null
}
}
private fun KtFile.getBuildScriptBlock(): KtBlockExpression? =
findScriptInitializer("buildscript")?.getBlock() ?: addTopLevelBlock("buildscript", true)
@@ -222,24 +341,24 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
findBlock("dependencies") ?: addBlock("dependencies")
private fun KtBlockExpression.addRepositoryIfMissing(version: String): KtCallExpression? {
val repository = getRepositoryForVersion(version)
val snippet = when {
repository != null -> repository.toKotlinRepositorySnippet()
!isRepositoryConfigured(text) -> MAVEN_CENTRAL
else -> return null
}
val snippet = getRepositorySnippet(version) ?: return null
return addExpressionIfMissing(snippet) as? KtCallExpression
}
private fun KtBlockExpression.addMavenCentralIfMissing(): KtCallExpression? =
if (!isRepositoryConfigured(text)) addExpressionIfMissing(MAVEN_CENTRAL) as? KtCallExpression else null
private fun KtBlockExpression.findOrCreateBlock(name: String, first: Boolean = false) = findBlock(name) ?: addBlock(name, first)
private fun KtBlockExpression.addPluginToClassPathIfMissing(): KtCallExpression? =
addExpressionIfMissing(getKotlinGradlePluginClassPathSnippet()) as? KtCallExpression
private fun KtBlockExpression.addBlock(name: String): KtBlockExpression? {
return add(psiFactory.createExpression("$name {\n}"))
fun getKotlinGradlePluginClassPathSnippet(): String =
"classpath(${getKotlinModuleDependencySnippet("gradle-plugin", "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME")})"
private fun KtBlockExpression.addBlock(name: String, first: Boolean = false): KtBlockExpression? {
return psiFactory.createExpression("$name {\n}")
.let { if (first) addAfter(it, null) else add(it) }
?.apply { addNewLinesIfNeeded() }
?.cast<KtCallExpression>()
?.getBlock()
@@ -277,11 +396,11 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
private fun KtFile.addImportIfMissing(path: String): KtImportDirective =
importDirectives.find { it.importPath?.pathStr == path } ?: importList?.add(
psiFactory.createImportDirective(
ImportPath.fromString(
path
psiFactory.createImportDirective(
ImportPath.fromString(
path
)
)
)
) as KtImportDirective
private fun KtBlockExpression.addExpressionAfterIfMissing(text: String, after: PsiElement): KtExpression = addStatementIfMissing(text) {
@@ -307,7 +426,7 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
text: String,
crossinline factory: (String) -> PsiElement
): T {
statements.find { it.text == text }?.let {
statements.find { StringUtil.equalsIgnoreWhitespaces(it.text, text) }?.let {
return it as T
}
@@ -324,10 +443,17 @@ class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBui
private val STDLIB_ARTIFACT_PREFIX = "org.jetbrains.kotlin:kotlin-stdlib"
val GSK_KOTLIN_VERSION_PROPERTY_NAME = "kotlin_version"
fun getKotlinGradlePluginClassPathSnippet(): String = "classpath(${getKotlinModuleDependencySnippet("gradle-plugin")})"
fun getKotlinGradlePluginClassPathSnippet(): String =
"classpath(${getKotlinModuleDependencySnippet("gradle-plugin", "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME")})"
fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String =
"kotlinModule(\"${artifactId.removePrefix("kotlin-")}\", ${version?.let { "\"$it\"" } ?: GSK_KOTLIN_VERSION_PROPERTY_NAME})"
fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String {
val moduleName = artifactId.removePrefix("kotlin-")
return when (version) {
null -> "kotlin(\"$moduleName\")"
"\$$GSK_KOTLIN_VERSION_PROPERTY_NAME" -> "kotlinModule(\"$moduleName\", $GSK_KOTLIN_VERSION_PROPERTY_NAME)"
else -> "kotlinModule(\"$moduleName\", ${"\"$version\""})"
}
}
fun getCompileDependencySnippet(
groupId: String,
@@ -18,14 +18,15 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.framework.FrameworkTypeEx
import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider
import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel
import com.intellij.openapi.externalSystem.model.project.ProjectId
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModifiableModelsProvider
import com.intellij.openapi.roots.ModifiableRootModel
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.GSK_KOTLIN_VERSION_PROPERTY_NAME
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getCompileDependencySnippet
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getKotlinGradlePluginClassPathSnippet
import org.jetbrains.kotlin.idea.configuration.KotlinBuildScriptManipulator.Companion.getKotlinModuleDependencySnippet
import org.jetbrains.kotlin.idea.versions.*
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.frameworkSupport.KotlinDslGradleFrameworkSupportProvider
@@ -42,6 +43,8 @@ abstract class KotlinDslGradleKotlinFrameworkSupportProvider(
override fun createProvider(): FrameworkSupportInModuleProvider = this@KotlinDslGradleKotlinFrameworkSupportProvider
}
override fun createConfigurable(model: FrameworkSupportModel) = KotlinGradleFrameworkSupportInModuleConfigurable(model, this)
override fun addSupport(
projectId: ProjectId,
module: Module,
@@ -55,38 +58,59 @@ abstract class KotlinDslGradleKotlinFrameworkSupportProvider(
kotlinVersion = LAST_SNAPSHOT_VERSION
}
if (additionalRepository != null) {
val repository = additionalRepository.toKotlinRepositorySnippet()
buildScriptData.addBuildscriptRepositoriesDefinition(repository)
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(repository)
}
val useNewSyntax = buildScriptData.gradleVersion >= MIN_GRADLE_VERSION_FOR_NEW_PLUGIN_SYNTAX
if (useNewSyntax) {
if (additionalRepository != null) {
val repository = additionalRepository.toKotlinRepositorySnippet()
updateSettingsScript(module) {
with(KotlinWithGradleConfigurator.getManipulator(it)) {
addPluginRepository(additionalRepository)
addMavenCentralPluginRepository()
}
}
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(repository)
}
buildScriptData
.addPropertyDefinition("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra")
.addPluginDefinition(getPluginDefinition())
.addBuildscriptRepositoriesDefinition("mavenCentral()")
.addRepositoriesDefinition("mavenCentral()")
// TODO: in gradle > 4.1 this could be single declaration e.g. 'val kotlin_version: String by extra { "1.1.11" }'
.addBuildscriptPropertyDefinition("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra\n $GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$kotlinVersion\"")
.addDependencyNotation(getRuntimeLibrary(rootModel))
.addBuildscriptDependencyNotation(getKotlinGradlePluginClassPathSnippet())
buildScriptData
.addPluginDefinitionInPluginsGroup(getPluginDefinition() + " version \"$kotlinVersion\"")
.addDependencyNotation(getRuntimeLibrary(rootModel, null))
} else {
if (additionalRepository != null) {
val repository = additionalRepository.toKotlinRepositorySnippet()
buildScriptData.addBuildscriptRepositoriesDefinition(repository)
buildScriptData.addRepositoriesDefinition("mavenCentral()")
buildScriptData.addRepositoriesDefinition(repository)
}
buildScriptData
.addPropertyDefinition("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra")
.addPluginDefinition(getOldSyntaxPluginDefinition())
.addBuildscriptRepositoriesDefinition("mavenCentral()")
.addRepositoriesDefinition("mavenCentral()")
// TODO: in gradle > 4.1 this could be single declaration e.g. 'val kotlin_version: String by extra { "1.1.11" }'
.addBuildscriptPropertyDefinition("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra\n $GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$kotlinVersion\"")
.addDependencyNotation(getRuntimeLibrary(rootModel, "\$$GSK_KOTLIN_VERSION_PROPERTY_NAME"))
.addBuildscriptDependencyNotation(getKotlinGradlePluginClassPathSnippet())
}
}
private fun RepositoryDescription.toKotlinRepositorySnippet() = "maven { setUrl(\"$url\") }"
protected abstract fun getRuntimeLibrary(rootModel: ModifiableRootModel): String
protected abstract fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?): String
protected abstract fun getOldSyntaxPluginDefinition(): String
protected abstract fun getPluginDefinition(): String
}
class KotlinDslGradleKotlinJavaFrameworkSupportProvider :
KotlinDslGradleKotlinFrameworkSupportProvider("KOTLIN", "Kotlin (Java)", KotlinIcons.SMALL_LOGO) {
override fun getPluginDefinition() = "plugin(\"${KotlinGradleModuleConfigurator.KOTLIN}\")"
override fun getOldSyntaxPluginDefinition() = "plugin(\"${KotlinGradleModuleConfigurator.KOTLIN}\")"
override fun getPluginDefinition() = "kotlin(\"jvm\")"
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
getCompileDependencySnippet(KOTLIN_GROUP_ID, getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion()))
override fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?) =
"compile(${getKotlinModuleDependencySnippet(getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion()), version)})"
override fun addSupport(
projectId: ProjectId,
@@ -108,8 +132,22 @@ class KotlinDslGradleKotlinJavaFrameworkSupportProvider :
class KotlinDslGradleKotlinJSFrameworkSupportProvider :
KotlinDslGradleKotlinFrameworkSupportProvider("KOTLIN_JS", "Kotlin (JavaScript)", KotlinIcons.JS) {
override fun getPluginDefinition(): String = "plugin(\"${KotlinJsGradleModuleConfigurator.KOTLIN_JS}\")"
override fun getOldSyntaxPluginDefinition(): String = "plugin(\"${KotlinJsGradleModuleConfigurator.KOTLIN_JS}\")"
override fun getPluginDefinition(): String = "id(\"kotlin2js\")"
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
getCompileDependencySnippet(KOTLIN_GROUP_ID, MAVEN_JS_STDLIB_ID.removePrefix("kotlin-"))
override fun getRuntimeLibrary(rootModel: ModifiableRootModel, version: String?) =
"compile(${getKotlinModuleDependencySnippet(MAVEN_JS_STDLIB_ID.removePrefix("kotlin-"), version)})"
override fun addSupport(
projectId: ProjectId,
module: Module,
rootModel: ModifiableRootModel,
modifiableModelsProvider: ModifiableModelsProvider,
buildScriptData: BuildScriptDataBuilder
) {
super.addSupport(projectId, module, rootModel, modifiableModelsProvider, buildScriptData)
updateSettingsScript(module) {
KotlinWithGradleConfigurator.getManipulator(it).addResolutionStrategy("kotlin2js")
}
}
}
@@ -37,6 +37,9 @@ class KotlinGradleModuleConfigurator : KotlinWithGradleConfigurator() {
override val kotlinPluginName: String
get() = KOTLIN
override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String =
if (forKotlinDsl) "kotlin(\"jvm\")" else "id 'org.jetbrains.kotlin.jvm'"
override fun getJvmTarget(sdk: Sdk?, version: String) = getDefaultJvmTarget(sdk, version)?.description
override fun configureModule(
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModifiableRootModel
@@ -27,11 +26,11 @@ import com.intellij.openapi.roots.ui.configuration.ModulesProvider
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory
import javax.swing.Icon
class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
@@ -65,28 +64,31 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
val module = modifiableRootModel.module
val buildScriptData = getBuildScriptData(module) ?: return
val sdk = modifiableRootModel.sdk
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, sdk)
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, module, sdk, true)
}
}
override fun setupModule(module: Module) {
super.setupModule(module)
try {
module.gradleModuleBuilder = this
val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return
setupCommonModule(module, contentRoot)
setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider(), jdk)
setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider())
super.setupModule(module)
val settingsGradle = contentRoot.findChild("settings.gradle")
settingsGradle?.let {
module.project.executeCommand("Update settings.gradle") {
val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand
val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return
setupCommonModule(module, contentRoot)
setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider(), jdk)
setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider())
updateSettingsScript(module) {
val includedModules = listOfNotNull(commonModuleName, jvmModuleName, jsModuleName).filter { it.isNotEmpty() }
if (includedModules.isNotEmpty()) {
doc.insertString(doc.textLength, includedModules.joinToString(prefix = "include ") { "'$it'" })
val includeCall = GroovyPsiElementFactory.getInstance(module.project)
.createExpressionFromText(includedModules.joinToString(prefix = "include ") { "'$it'" })
it.add(includeCall)
}
FileDocumentManager.getInstance().saveDocument(doc)
}
} finally {
flushSettingsGradleCopy(module)
}
}
@@ -110,7 +112,7 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
rootModule: Module,
contentRoot: VirtualFile
) = setupChildModule(rootModule, contentRoot, commonModuleName) { builder, sdk ->
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, sdk)
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, rootModule, sdk, true)
}
private fun setupPlatformModule(
@@ -120,7 +122,7 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
supportProvider: GradleKotlinFrameworkSupportProvider,
sdk: Sdk? = null
) = setupChildModule(rootModule, contentRoot, platformModuleName, sdk) { builder, finalSdk ->
supportProvider.addSupport(builder, finalSdk)
supportProvider.addSupport(builder, rootModule, finalSdk, !commonModuleName.isNullOrEmpty())
val dependency = commonModuleName ?: ""
builder.addDependencyNotation("expectedBy project(\":$dependency\")")
}
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.idea.versions.MAVEN_JS_STDLIB_ID
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.resolve.TargetPlatform
@@ -26,9 +28,26 @@ class KotlinJsGradleModuleConfigurator : KotlinWithGradleConfigurator() {
override val presentableText: String = "JavaScript with Gradle"
override val targetPlatform: TargetPlatform = JsPlatform
override val kotlinPluginName: String = KOTLIN_JS
override fun getKotlinPluginExpression(forKotlinDsl: Boolean): String =
if (forKotlinDsl) "id(\"kotlin2js\")" else "id 'kotlin2js'"
override fun getMinimumSupportedVersion() = "1.1.0"
override fun getStdlibArtifactName(sdk: Sdk?, version: String): String = MAVEN_JS_STDLIB_ID
override fun addElementsToFile(file: PsiFile, isTopLevelProjectFile: Boolean, version: String): Boolean {
if (getManipulator(file).useNewSyntax(kotlinPluginName)) {
val settingsPsiFile = if (isTopLevelProjectFile) {
file.module?.getTopLevelBuildScriptSettingsPsiFile()
} else {
file.module?.getBuildScriptSettingsPsiFile()
}
if (settingsPsiFile != null) {
getManipulator(settingsPsiFile).addResolutionStrategy("kotlin2js")
}
}
return super.addElementsToFile(file, isTopLevelProjectFile, version)
}
companion object {
val KOTLIN_JS = "kotlin2js"
}
@@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.WritingAccessProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.CoroutineSupport
import org.jetbrains.kotlin.config.LanguageFeature
@@ -82,7 +83,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
protected open fun getMinimumSupportedVersion() = "1.0.0"
private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName)
protected fun PsiFile.isKtDsl() = this is KtFile
private fun isFileConfigured(buildScript: PsiFile): Boolean = with(getManipulator(buildScript)) {
isConfiguredWithOldSyntax(kotlinPluginName) || isConfigured(getKotlinPluginExpression(buildScript.isKtDsl()))
}
@JvmSuppressWildcards
override fun configure(project: Project, excludeModules: Collection<Module>) {
@@ -146,10 +151,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
val jvmTarget = getJvmTarget(sdk, version)
return getManipulator(file).configureModuleBuildScript(
kotlinPluginName,
getStdlibArtifactName(sdk, version),
version,
jvmTarget
kotlinPluginName,
getKotlinPluginExpression(file.isKtDsl()),
getStdlibArtifactName(sdk, version),
version,
jvmTarget
)
}
@@ -158,6 +164,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
protected abstract val kotlinPluginName: String
protected abstract fun getKotlinPluginExpression(forKotlinDsl: Boolean): String
protected open fun addElementsToFile(
file: PsiFile,
@@ -165,7 +172,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
version: String
): Boolean {
if (!isTopLevelProjectFile) {
var wasModified = configureProjectFile(file, version)
var wasModified = getManipulator(file).configureProjectBuildScript(kotlinPluginName, version)
wasModified = wasModified or configureModuleBuildScript(file, version)
return wasModified
}
@@ -251,21 +258,20 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
}
companion object {
fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) {
is KtFile -> KotlinBuildScriptManipulator(file)
is GroovyFile -> GroovyBuildScriptManipulator(file)
fun getManipulator(file: PsiFile, preferNewSyntax: Boolean = true): GradleBuildScriptManipulator<*> = when (file) {
is KtFile -> KotlinBuildScriptManipulator(file, preferNewSyntax)
is GroovyFile -> GroovyBuildScriptManipulator(file, preferNewSyntax)
else -> error("Unknown build script file type!")
}
val GROUP_ID = "org.jetbrains.kotlin"
val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
private val KOTLIN_SETTINGS_SCRIPT_NAME = "settings.gradle.kts"
fun getGroovyDependencySnippet(artifactName: String, scope: String) =
"$scope \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
fun getGroovyDependencySnippet(artifactName: String, scope: String, withVersion: Boolean) =
"$scope \"org.jetbrains.kotlin:$artifactName${if (withVersion) ":\$kotlin_version" else ""}\""
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
@@ -320,28 +326,37 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
}
}
fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version)
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project)
private fun Module.getBuildScriptPsiFile() =
getBuildScriptFile(GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(project)
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) }
fun Module.getBuildScriptSettingsPsiFile() =
getBuildScriptSettingsFile(GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
private fun Module.getBuildScriptFile(): File? {
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let {
findBuildGradleFile(it, GradleConstants.DEFAULT_SCRIPT_NAME, KOTLIN_BUILD_SCRIPT_NAME)?.getPsiFile(this)
}
fun Module.getTopLevelBuildScriptSettingsPsiFile() =
ExternalSystemApiUtil.getExternalRootProjectPath(this)?.let { externalProjectPath ->
findBuildGradleFile(externalProjectPath, GradleConstants.SETTINGS_FILE_NAME, KOTLIN_SETTINGS_SCRIPT_NAME)?.getPsiFile(project)
}
private fun Module.getBuildScriptFile(vararg fileNames: String): File? {
val moduleDir = File(moduleFilePath).parent
findBuildGradleFile(moduleDir)?.let {
findBuildGradleFile(moduleDir, *fileNames)?.let {
return it
}
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
findBuildGradleFile(root.path)?.let {
findBuildGradleFile(root.path, *fileNames)?.let {
return it
}
}
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
findBuildGradleFile(externalProjectPath)?.let {
findBuildGradleFile(externalProjectPath, *fileNames)?.let {
return it
}
}
@@ -349,9 +364,20 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return null
}
private fun findBuildGradleFile(path: String): File? =
File(path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME).takeIf { it.exists() }
?: File(path + "/" + KOTLIN_BUILD_SCRIPT_NAME).takeIf { it.exists() }
private fun Module.getBuildScriptSettingsFile(vararg fileNames: String): File? {
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
return generateSequence(externalProjectPath) {
PathUtil.getParentPath(it).let { if (it.isBlank()) null else it }
}.mapNotNull {
findBuildGradleFile(it, *fileNames)
}.firstOrNull()
}
return null
}
private fun findBuildGradleFile(path: String, vararg fileNames: String): File? =
fileNames.asSequence().map { File(path + "/" + it) }.firstOrNull { it.exists() }
private fun File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
PsiManager.getInstance(project).findFile(it)
@@ -0,0 +1,88 @@
/*
* 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.configuration
import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable
import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel
import com.intellij.openapi.externalSystem.model.project.ProjectId
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModifiableModelsProvider
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
internal var Module.gradleModuleBuilder: GradleModuleBuilder? by UserDataProperty(Key.create("GRADLE_MODULE_BUILDER"))
private var Module.editableSettingsPsiFileCopy: PsiFile? by UserDataProperty(Key.create("EDITABLE_SETTINGS_PSI_FILE_COPY"))
internal fun findSettingsGradleFile(module: Module): VirtualFile? {
val contentEntryPath = module.gradleModuleBuilder?.contentEntryPath ?: return null
if (contentEntryPath.isEmpty()) return null
val contentRootDir = File(contentEntryPath)
val modelContentRootDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(contentRootDir) ?: return null
return modelContentRootDir.findChild(GradleConstants.SETTINGS_FILE_NAME)
?: module.project.baseDir.findChild(GradleConstants.SETTINGS_FILE_NAME)
}
// Circumvent write actions and modify the file directly
// TODO: Get rid of this hack when IDEA API allows manipulation of settings script similarly to the main script itself
internal fun updateSettingsScript(module: Module, updater: (PsiFile) -> Unit) {
val storedCopy = module.editableSettingsPsiFileCopy
val settingsPsiCopy = storedCopy ?: findSettingsGradleFile(module)?.toPsiFile(module.project)?.copied() ?: return
if (storedCopy == null) {
module.editableSettingsPsiFileCopy = settingsPsiCopy
}
updater(settingsPsiCopy)
}
internal fun flushSettingsGradleCopy(module: Module) {
try {
val settingsFile = findSettingsGradleFile(module)
val settingsPsiCopy = module.editableSettingsPsiFileCopy
if (settingsPsiCopy != null && settingsFile != null) {
CodeStyleManager.getInstance(module.project).reformat(settingsPsiCopy)
VfsUtil.saveText(settingsFile, settingsPsiCopy.text)
}
} finally {
module.gradleModuleBuilder = null
module.editableSettingsPsiFileCopy = null
}
}
class KotlinGradleFrameworkSupportInModuleConfigurable(
private val model: FrameworkSupportModel,
private val supportProvider: GradleFrameworkSupportProvider
) : FrameworkSupportInModuleConfigurable() {
override fun createComponent() = supportProvider.createComponent()
override fun addSupport(
module: Module,
rootModel: ModifiableRootModel,
modifiableModelsProvider: ModifiableModelsProvider
) {
val buildScriptData = GradleModuleBuilder.getBuildScriptData(module)
if (buildScriptData != null) {
val builder = model.moduleBuilder
val projectId = (builder as? GradleModuleBuilder)?.projectId ?: ProjectId(null, module.name, null)
try {
module.gradleModuleBuilder = builder as? GradleModuleBuilder
supportProvider.addSupport(projectId, module, rootModel, modifiableModelsProvider, buildScriptData)
} finally {
flushSettingsGradleCopy(module)
}
}
}
}