Implement Kotlin configurator for GSK

#KT-14965 Fixed
This commit is contained in:
Vyacheslav Gerasimov
2017-05-31 14:30:04 +03:00
parent 2116877991
commit 2ec1a4311a
32 changed files with 1266 additions and 337 deletions
@@ -65,7 +65,9 @@ val EAP_12_REPOSITORY = RepositoryDescription(
"https://bintray.com/kotlin/kotlin-eap-1.2/kotlin/",
isSnapshot = false)
fun RepositoryDescription.toRepositorySnippet() = "maven {\nurl '$url'\n}"
fun RepositoryDescription.toGroovyRepositorySnippet() = "maven {\nurl '$url'\n}"
fun RepositoryDescription.toKotlinRepositorySnippet() = "maven {\nsetUrl(\"$url\")\n}"
fun getRepositoryForVersion(version: String): RepositoryDescription? = when {
isSnapshot(version) -> SNAPSHOT_REPOSITORY
@@ -145,7 +147,7 @@ fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
fun allConfigurators() = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
fun getNonConfiguredModules(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
fun getCanBeConfiguredModules(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
return project.allModules()
.filter { module -> configurator.canConfigure(module) }
.excludeSourceRootModules()
@@ -185,12 +187,12 @@ val Module.externalProjectId: String?
val Module.externalProjectPath: String?
get() = ExternalSystemApiUtil.getExternalProjectPath(this)
fun getNonConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
val modules = getConfigurableModulesWithKotlinFiles(project)
return modules.filter { module -> configurator.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
}
fun getNonConfiguredModulesWithKotlinFiles(project: Project, excludeModules: Collection<Module> = emptyList()): Collection<Module> {
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, excludeModules: Collection<Module> = emptyList()): Collection<Module> {
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project) - excludeModules
val configurators = allConfigurators()
return modulesWithKotlinFiles.filter { module ->
@@ -0,0 +1,236 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.configuration
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
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
fun getKotlinScriptDependencySnippet(artifactName: String): String =
"compile(${getKotlinModuleDependencySnippet(artifactName)})"
fun getKotlinModuleDependencySnippet(artifactName: String): String =
"kotlinModule(\"${artifactName.removePrefix("kotlin-")}\", $GSK_KOTLIN_VERSION_PROPERTY_NAME)"
fun KtFile.containsCompileStdLib(): Boolean =
findScriptInitializer("dependencies")?.getBlock()?.findCompileStdLib() != null
fun KtFile.containsApplyKotlinPlugin(pluginName: String): Boolean =
findScriptInitializer("apply")?.getBlock()?.findPlugin(pluginName) != null
fun KtBlockExpression.findCompileStdLib(): KtCallExpression? {
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
it.calleeExpression?.text == "compile" && (it.valueArguments.firstOrNull()?.getArgumentExpression()?.isKotlinStdLib() ?: false)
}
}
fun KtFile.getRepositoriesBlock(): KtBlockExpression? =
findScriptInitializer("repositories")?.getBlock() ?: addTopLevelBlock("repositories")
fun KtFile.getDependenciesBlock(): KtBlockExpression? =
findScriptInitializer("dependencies")?.getBlock() ?: addTopLevelBlock("dependencies")
fun KtFile.createApplyBlock(): KtBlockExpression? {
val apply = psiFactory.createScriptInitializer("apply {\n}")
val plugins = findScriptInitializer("plugins")
val addedElement = plugins?.addSibling(apply) ?: addToScriptBlock(apply)
addedElement?.addNewLinesIfNeeded()
return (addedElement as? KtScriptInitializer)?.getBlock()
}
fun KtFile.getApplyBlock(): KtBlockExpression? = findScriptInitializer("apply")?.getBlock() ?: createApplyBlock()
private fun KtExpression.isKotlinStdLib(): Boolean = when (this) {
is KtCallExpression -> calleeExpression?.text == "kotlinModule" &&
valueArguments.firstOrNull()?.getArgumentExpression()?.text == "\"stdlib\""
is KtStringTemplateExpression -> text.startsWith("\"org.jetbrains.kotlin:kotlin-stdlib:")
else -> false
}
private fun KtBlockExpression.findPlugin(pluginName: String): KtCallExpression? {
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
it.calleeExpression?.text == "plugin" && it.valueArguments.firstOrNull()?.text == "\"$pluginName\""
}
}
fun KtBlockExpression.createPluginIfMissing(pluginName: String): KtCallExpression? =
findPlugin(pluginName) ?: addExpressionIfMissing("plugin(\"$pluginName\")") as? KtCallExpression
fun changeCoroutineConfiguration(buildScriptFile: KtFile, coroutineOption: String): PsiElement? {
val snippet = "experimental.coroutines = Coroutines.${coroutineOption.toUpperCase()}"
val kotlinBlock = buildScriptFile.findScriptInitializer("kotlin")?.getBlock() ?:
buildScriptFile.addTopLevelBlock("kotlin") ?: return null
buildScriptFile.addImportIfMissing("org.jetbrains.kotlin.gradle.dsl.Coroutines")
val statement = kotlinBlock.statements.find { it.text.startsWith("experimental.coroutines") }
return if (statement != null) {
statement.replace(buildScriptFile.psiFactory.createExpression(snippet))
}
else {
kotlinBlock.add(buildScriptFile.psiFactory.createExpression(snippet)).apply { addNewLinesIfNeeded() }
}
}
fun KtFile.changeKotlinTaskParameter(parameterName: String, parameterValue: String, forTests: Boolean): PsiElement? {
val snippet = "$parameterName = \"$parameterValue\""
val taskName = if (forTests) "compileTestKotlin" else "compileKotlin"
val optionsBlock = findScriptInitializer("$taskName.kotlinOptions")?.getBlock()
return if (optionsBlock != null) {
val assignment = optionsBlock.statements.find {
(it as? KtBinaryExpression)?.left?.text == parameterName
}
if (assignment != null) {
assignment.replace(psiFactory.createExpression(snippet))
}
else {
optionsBlock.addExpressionIfMissing(snippet)
}
}
else {
addImportIfMissing("org.jetbrains.kotlin.gradle.tasks.KotlinCompile")
script?.blockExpression?.addDeclarationIfMissing("val $taskName: KotlinCompile by tasks")
addTopLevelBlock("$taskName.kotlinOptions")?.addExpressionIfMissing(snippet)
}
}
fun KtFile.getBuildScriptBlock(): KtBlockExpression? =
findScriptInitializer("buildscript")?.getBlock() ?: addTopLevelBlock("buildscript", true)
fun KtBlockExpression.getRepositoriesBlock(): KtBlockExpression? =
findBlock("repositories") ?: addBlock("repositories")
fun KtBlockExpression.getDependenciesBlock(): KtBlockExpression? =
findBlock("dependencies") ?: addBlock("dependencies")
fun KtBlockExpression.addRepositoryIfMissing(version: String): KtCallExpression? {
val repository = getRepositoryForVersion(version)
val snippet = when {
repository != null -> repository.toKotlinRepositorySnippet()
!isRepositoryConfigured() -> MAVEN_CENTRAL
else -> return null
}
return addExpressionIfMissing(snippet) as? KtCallExpression
}
private fun KtBlockExpression.isRepositoryConfigured(): Boolean {
return text.contains(MAVEN_CENTRAL) || text.contains(JCENTER)
}
fun KtBlockExpression.addPluginToClassPathIfMissing(): KtCallExpression? =
addExpressionIfMissing("classpath(${getKotlinModuleDependencySnippet("gradle-plugin")})") as? KtCallExpression
private fun KtFile.findScriptInitializer(startsWith: String): KtScriptInitializer? =
PsiTreeUtil.findChildrenOfType(this, KtScriptInitializer::class.java).find { it.text.startsWith(startsWith) }
private fun KtBlockExpression.findBlock(name: String): KtBlockExpression? {
return getChildrenOfType<KtCallExpression>().find {
it.calleeExpression?.text == name &&
it.valueArguments.singleOrNull()?.getArgumentExpression() is KtLambdaExpression
}?.getBlock()
}
private fun KtScriptInitializer.getBlock(): KtBlockExpression? =
PsiTreeUtil.findChildOfType<KtCallExpression>(this, KtCallExpression::class.java)?.getBlock()
private fun KtCallExpression.getBlock(): KtBlockExpression? =
(valueArguments.singleOrNull()?.getArgumentExpression() as? KtLambdaExpression)?.bodyExpression
private fun KtBlockExpression.addBlock(name: String): KtBlockExpression? {
return add(psiFactory.createExpression("$name {\n}"))
?.apply { addNewLinesIfNeeded() }
?.cast<KtCallExpression>()
?.getBlock()
}
private fun KtFile.addTopLevelBlock(name: String, first: Boolean = false): KtBlockExpression? {
val scriptInitializer = psiFactory.createScriptInitializer("$name {\n}")
val addedElement = addToScriptBlock(scriptInitializer, first) as? KtScriptInitializer
addedElement?.addNewLinesIfNeeded()
return addedElement?.getBlock()
}
private fun PsiElement.addSibling(element: PsiElement): PsiElement = parent.addAfter(element, this)
private fun PsiElement.addNewLineBefore(lineBreaks: Int = 1) {
parent.addBefore(psiFactory.createNewLine(lineBreaks), this)
}
private fun PsiElement.addNewLineAfter(lineBreaks: Int = 1) {
parent.addAfter(psiFactory.createNewLine(lineBreaks), this)
}
private fun PsiElement.addNewLinesIfNeeded(lineBreaks: Int = 1) {
if (prevSibling != null && prevSibling.text.isNotBlank()) {
addNewLineBefore(lineBreaks)
}
if (nextSibling != null && nextSibling.text.isNotBlank()) {
addNewLineAfter(lineBreaks)
}
}
private fun KtFile.addToScriptBlock(element: PsiElement, first: Boolean = false): PsiElement? =
if (first) script?.blockExpression?.addAfter(element, null) else script?.blockExpression?.add(element)
private fun KtFile.addImportIfMissing(path: String): KtImportDirective =
importDirectives.find { it.importPath?.pathStr == path } ?:
importList?.add(psiFactory.createImportDirective(ImportPath.fromString(path))) as KtImportDirective
fun KtBlockExpression.addExpressionAfterIfMissing(text: String, after: PsiElement): KtExpression = addStatementIfMissing(text) {
psiFactory.createExpression(it).let { created ->
addAfter(created, after)
}
}
fun KtBlockExpression.addExpressionIfMissing(text: String, first: Boolean = false): KtExpression = addStatementIfMissing(text) {
psiFactory.createExpression(it).let { created ->
if(first) addAfter(created, null) else add(created)
}
}
fun KtBlockExpression.addDeclarationIfMissing(text: String, first: Boolean = false): KtDeclaration = addStatementIfMissing(text) {
psiFactory.createDeclaration<KtDeclaration>(it).let { created ->
if(first) addAfter(created, null) else add(created)
}
}
private inline fun <reified T: PsiElement> KtBlockExpression.addStatementIfMissing(
text: String,
crossinline factory: (String) -> PsiElement): T {
statements.find { it.text == text }?.let {
return it as T
}
return factory(text).apply { addNewLinesIfNeeded() } as T
}
private fun KtPsiFactory.createScriptInitializer(text: String): KtScriptInitializer =
createFile("dummy.kts", text).script?.blockExpression?.firstChild as KtScriptInitializer
private val PsiElement.psiFactory: KtPsiFactory
get() = KtPsiFactory(this)
private val MAVEN_CENTRAL = "mavenCentral()"
private val JCENTER = "jcenter()"
val GSK_KOTLIN_VERSION_PROPERTY_NAME = "kotlin_version"
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.versions.MAVEN_JS_STDLIB_ID
import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
import org.jetbrains.kotlin.idea.versions.getDefaultJvmTarget
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider
import javax.swing.Icon
@@ -50,7 +51,7 @@ abstract class GradleKotlinFrameworkSupportProvider(val frameworkTypeId: String,
}
if (additionalRepository != null) {
val oneLineRepository = additionalRepository.toRepositorySnippet().replace('\n', ' ')
val oneLineRepository = additionalRepository.toGroovyRepositorySnippet().replace('\n', ' ')
buildScriptData.addBuildscriptRepositoriesDefinition(oneLineRepository)
buildScriptData.addRepositoriesDefinition("mavenCentral()")
@@ -74,10 +75,11 @@ abstract class GradleKotlinFrameworkSupportProvider(val frameworkTypeId: String,
}
class GradleKotlinJavaFrameworkSupportProvider : GradleKotlinFrameworkSupportProvider("KOTLIN", "Kotlin (Java)") {
override fun getPluginDefinition() = KotlinGradleModuleConfigurator.APPLY_KOTLIN
override fun getPluginDefinition() =
KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(KotlinGradleModuleConfigurator.KOTLIN)
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
KotlinWithGradleConfigurator.getRuntimeLibraryForSdk(rootModel.sdk, bundledRuntimeVersion())
KotlinWithGradleConfigurator.getGroovyDependencySnippet(getStdlibArtifactId(rootModel.sdk, bundledRuntimeVersion()))
override fun addSupport(module: Module, rootModel: ModifiableRootModel, modifiableModelsProvider: ModifiableModelsProvider, buildScriptData: BuildScriptDataBuilder) {
super.addSupport(module, rootModel, modifiableModelsProvider, buildScriptData)
@@ -90,10 +92,9 @@ class GradleKotlinJavaFrameworkSupportProvider : GradleKotlinFrameworkSupportPro
}
class GradleKotlinJSFrameworkSupportProvider : GradleKotlinFrameworkSupportProvider("KOTLIN_JS", "Kotlin (JavaScript)") {
override fun getPluginDefinition(): String {
return KotlinJsGradleModuleConfigurator.APPLY_KOTLIN_JS
}
override fun getPluginDefinition(): String =
KotlinWithGradleConfigurator.getGroovyApplyPluginDirective(KotlinJsGradleModuleConfigurator.KOTLIN_JS)
override fun getRuntimeLibrary(rootModel: ModifiableRootModel) =
KotlinWithGradleConfigurator.getDependencySnippet(MAVEN_JS_STDLIB_ID)
KotlinWithGradleConfigurator.getGroovyDependencySnippet(MAVEN_JS_STDLIB_ID)
}
@@ -32,14 +32,13 @@ class KotlinGradleModuleConfigurator internal constructor() : KotlinWithGradleCo
override val presentableText: String
get() = "Gradle"
override val applyPluginDirective: String
get() = APPLY_KOTLIN
override val kotlinPluginName: String
get() = KOTLIN
override fun getJvmTarget(sdk: Sdk?, version: String) = getDefaultJvmTarget(sdk, version)?.description
companion object {
val NAME = "gradle"
val APPLY_KOTLIN = "apply plugin: 'kotlin'"
val KOTLIN = "kotlin"
}
}
@@ -25,15 +25,11 @@ class KotlinJsGradleModuleConfigurator : KotlinWithGradleConfigurator() {
override val name: String = "gradle-js"
override val presentableText: String = "Gradle (JavaScript)"
override val targetPlatform: TargetPlatform = JsPlatform
override val applyPluginDirective: String = APPLY_KOTLIN_JS
override val kotlinPluginName: String = KOTLIN_JS
override fun getMinimumSupportedVersion() = "1.1.0"
override fun getDependencyDirective(sdk: Sdk?, version: String): String {
return KotlinWithGradleConfigurator.getDependencySnippet(MAVEN_JS_STDLIB_ID)
}
override fun getStdlibArtifactName(sdk: Sdk?, version: String): String = MAVEN_JS_STDLIB_ID
companion object {
val APPLY_KOTLIN_JS = "apply plugin: 'kotlin2js'"
val KOTLIN_JS = "kotlin2js"
}
}
@@ -31,6 +31,7 @@ import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.WritingAccessProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.idea.KotlinPluginUtil
@@ -38,6 +39,9 @@ import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersi
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.versions.getStdlibArtifactId
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
@@ -63,17 +67,23 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return ConfigureKotlinStatus.CONFIGURED
}
val buildFiles = listOf(getBuildGradleFile(module.project, getModuleFilePath(module)),
getBuildGradleFile(module.project, getTopLevelProjectFilePath(module.project)))
.filterNotNull()
if (buildFiles.none { buildFile -> allGradleConfigurators.any { it.isFileConfigured(buildFile) } })
val buildFiles = listOf(module.getBuildScriptPsiFile(), module.project.getTopLevelBuildScriptPsiFile()).filterNotNull()
if (buildFiles.isEmpty()) {
return ConfigureKotlinStatus.NON_APPLICABLE
}
if (buildFiles.none { it.isConfiguredByAnyGradleConfigurator() }) {
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
}
return ConfigureKotlinStatus.BROKEN
}
private val allGradleConfigurators: Collection<KotlinWithGradleConfigurator>
get() = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME).filterIsInstance<KotlinWithGradleConfigurator>()
private fun PsiFile.isConfiguredByAnyGradleConfigurator() =
Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
.filterIsInstance<KotlinWithGradleConfigurator>()
.any { it.isFileConfigured(this) }
protected open fun isApplicable(module: Module): Boolean {
return KotlinPluginUtil.isGradleModule(module) && !KotlinPluginUtil.isAndroidGradleModule(module)
@@ -81,9 +91,18 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
protected open fun getMinimumSupportedVersion() = "1.0.0"
private fun isFileConfigured(projectGradleFile: GroovyFile): Boolean {
val fileText = projectGradleFile.text
return containsDirective(fileText, applyPluginDirective) &&
private fun isFileConfigured(projectGradleFile: PsiFile): Boolean = when(projectGradleFile) {
is GroovyFile -> isGradleFileConfigured(projectGradleFile)
is KtFile -> isKotlinFileConfigured(projectGradleFile)
else -> error("Unknown build script file type")
}
fun isKotlinFileConfigured(file: KtFile): Boolean =
file.containsApplyKotlinPlugin(kotlinPluginName) && file.containsCompileStdLib()
private fun isGradleFileConfigured(file: GroovyFile): Boolean {
val fileText = file.text
return containsDirective(fileText, getGroovyApplyPluginDirective(kotlinPluginName)) &&
fileText.contains("org.jetbrains.kotlin") &&
fileText.contains("kotlin-stdlib")
}
@@ -109,20 +128,20 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
fun configureWithVersion(project: Project,
modulesToConfigure: List<Module>,
kotlinVersion: String,
collector: NotificationMessageCollector): HashSet<GroovyFile> {
val changedFiles = HashSet<GroovyFile>()
val projectGradleFile = getBuildGradleFile(project, getTopLevelProjectFilePath(project))
if (projectGradleFile != null && canConfigureFile(projectGradleFile)) {
val isModified = changeGradleFile(projectGradleFile, true, kotlinVersion, collector)
collector: NotificationMessageCollector): HashSet<PsiFile> {
val changedFiles = HashSet<PsiFile>()
val buildScript = project.getTopLevelBuildScriptPsiFile()
if (buildScript != null && canConfigureFile(buildScript)) {
val isModified = changeBuildScript(buildScript, true, kotlinVersion, collector)
if (isModified) {
changedFiles.add(projectGradleFile)
changedFiles.add(buildScript)
}
}
for (module in modulesToConfigure) {
val file = getBuildGradleFile(project, getModuleFilePath(module))
val file = module.getBuildScriptPsiFile()
if (file != null && canConfigureFile(file)) {
val isModified = changeGradleFile(file, false, kotlinVersion, collector)
val isModified = changeBuildScript(file, false, kotlinVersion, collector)
if (isModified) {
changedFiles.add(file)
}
@@ -134,9 +153,34 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return changedFiles
}
protected fun addElementsToModuleFile(file: GroovyFile, version: String): Boolean {
protected fun addElementsToModuleFile(file: PsiFile, version: String): Boolean = when (file) {
is GroovyFile -> addElementsToModuleGroovyFile(file, version)
is KtFile -> addElementsToModuleGSKFile(file, version)
else -> error("Unknown build script file type")
}
protected fun addElementsToModuleGSKFile(file: KtFile, version: String): Boolean {
val originalText = file.text
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
file.script?.blockExpression?.addDeclarationIfMissing("val $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true)
file.getApplyBlock()?.createPluginIfMissing(kotlinPluginName)
file.getDependenciesBlock()?.addCompileStdlibIfMissing(sdk, version)
file.getRepositoriesBlock()?.addRepositoryIfMissing(version)
getJvmTarget(sdk, version)?.let {
file.changeKotlinTaskParameter("jvmTarget", it, forTests = false)
file.changeKotlinTaskParameter("jvmTarget", it, forTests = true)
}
return originalText != file.text
}
private fun KtBlockExpression.addCompileStdlibIfMissing(sdk: Sdk?, version: String): KtCallExpression? =
findCompileStdLib() ?:
addExpressionIfMissing(getKotlinScriptDependencySnippet(getStdlibArtifactName(sdk, version))) as? KtCallExpression
protected fun addElementsToModuleGroovyFile(file: GroovyFile, version: String): Boolean {
val oldText = file.text
val applyPluginDirective = getGroovyApplyPluginDirective(kotlinPluginName)
if (!containsDirective(file.text, applyPluginDirective)) {
val apply = GroovyPsiElementFactory.getInstance(file.project).createExpressionFromText(applyPluginDirective)
val applyStatement = getApplyStatement(file)
@@ -159,7 +203,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
val dependenciesBlock = getDependenciesBlock(file)
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
addExpressionInBlockIfNeeded(getDependencyDirective(sdk, version), dependenciesBlock, false)
addExpressionInBlockIfNeeded(getStdlibDependencyDirectiveForGroovy(sdk, version), dependenciesBlock, false)
val jvmTarget = getJvmTarget(sdk, version)
if (jvmTarget != null) {
changeKotlinTaskParameter(file, "jvmTarget", jvmTarget, forTests = false)
@@ -169,51 +213,48 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return file.text != oldText
}
protected open fun getDependencyDirective(sdk: Sdk?, version: String) = getRuntimeLibrary(sdk, version)
protected open fun getStdlibArtifactName(sdk: Sdk?, version: String) = getStdlibArtifactId(sdk, version)
private fun getStdlibDependencyDirectiveForGroovy(sdk: Sdk?, version: String) =
getGroovyDependencySnippet(getStdlibArtifactName(sdk, version))
protected open fun getJvmTarget(sdk: Sdk?, version: String): String? = null
protected abstract val applyPluginDirective: String
protected abstract val kotlinPluginName: String
protected open fun addElementsToFile(
groovyFile: GroovyFile,
file: PsiFile,
isTopLevelProjectFile: Boolean,
version: String
): Boolean {
if (!isTopLevelProjectFile) {
var wasModified = addElementsToProjectFile(groovyFile, version)
wasModified = wasModified or addElementsToModuleFile(groovyFile, version)
var wasModified = addElementsToProjectFile(file, version)
wasModified = wasModified or addElementsToModuleFile(file, version)
return wasModified
}
return false
}
fun changeGradleFile(
groovyFile: GroovyFile,
fun changeBuildScript(
file: PsiFile,
isTopLevelProjectFile: Boolean,
version: String,
collector: NotificationMessageCollector
): Boolean {
val isModified = groovyFile.project.executeWriteCommand("Configure build.gradle", null) {
val isModified = addElementsToFile(groovyFile, isTopLevelProjectFile, version)
val isModified = file.project.executeWriteCommand("Configure ${file.name}", null) {
val isModified = addElementsToFile(file, isTopLevelProjectFile, version)
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(groovyFile)
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(file)
isModified
}
val virtualFile = groovyFile.virtualFile
val virtualFile = file.virtualFile
if (virtualFile != null && isModified) {
collector.addMessage(virtualFile.path + " was modified")
}
return isModified
}
open fun getRuntimeLibrary(sdk: Sdk?, version: String): String {
return getRuntimeLibraryForSdk(sdk, version)
}
companion object {
private val VERSION_TEMPLATE = "\$VERSION$"
@@ -222,21 +263,25 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
private val MAVEN_CENTRAL = "mavenCentral()\n"
private val JCENTER = "jcenter()\n"
private val MAVEN_CENTRAL = "mavenCentral()"
private val JCENTER = "jcenter()"
private val VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE)
private val KOTLIN_BUILD_SCRIPT_NAME = "build.gradle.kts"
private fun containsDirective(fileText: String, directive: String): Boolean {
return fileText.contains(directive)
|| fileText.contains(directive.replace("\"", "'"))
|| fileText.contains(directive.replace("'", "\""))
}
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
val gradleFilePath = getModuleFilePath(module)
val gradleFile = getBuildGradleFile(module.project, gradleFilePath)
fun getGroovyDependencySnippet(artifactName: String) = "compile \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
val gradleFile = module.getBuildScriptPsiFile() as? GroovyFile
if (gradleFile != null && canConfigureFile(gradleFile)) {
gradleFile.project.executeWriteCommand("Add Kotlin library") {
val groovyScope = when (scope) {
@@ -274,8 +319,12 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
}
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? {
return changeBuildGradle(module) { gradleFile ->
changeCoroutineConfiguration(gradleFile, coroutineOption)
return changeBuildGradle(module) { buildScriptFile ->
when (buildScriptFile) {
is GroovyFile -> changeCoroutineConfiguration(buildScriptFile, coroutineOption)
is KtFile -> changeCoroutineConfiguration(buildScriptFile, coroutineOption)
else -> error("Unknown build script file type")
}
}
}
@@ -290,18 +339,36 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
}
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String? = null, forTests: Boolean): PsiElement? {
return changeBuildGradle(module) { gradleFile ->
return changeBuildGradle(module) { buildScriptFile ->
var result: PsiElement? = null
if (languageVersion != null) {
result = changeLanguageVersion(gradleFile, languageVersion, forTests)
result = when (buildScriptFile) {
is GroovyFile -> changeLanguageVersion(buildScriptFile, languageVersion, forTests)
is KtFile -> changeLanguageVersion(buildScriptFile, languageVersion, forTests)
else -> error("Unknown build script file type")
}
}
if (apiVersion != null) {
result = changeApiVersion(gradleFile, apiVersion, forTests)
result = when (buildScriptFile) {
is GroovyFile -> changeApiVersion(buildScriptFile, apiVersion, forTests)
is KtFile -> changeApiVersion(buildScriptFile, apiVersion, forTests)
else -> error("Unknown build script file type")
}
}
result
}
}
fun changeLanguageVersion(gradleFile: KtFile, languageVersion: String, forTests: Boolean): PsiElement? {
return gradleFile.changeKotlinTaskParameter("languageVersion", languageVersion, forTests)
}
fun changeApiVersion(gradleFile: KtFile, apiVersion: String, forTests: Boolean): PsiElement? {
return gradleFile.changeKotlinTaskParameter("apiVersion", apiVersion, forTests)
}
fun changeLanguageVersion(gradleFile: GroovyFile, languageVersion: String, forTests: Boolean): PsiElement? {
return changeKotlinTaskParameter(gradleFile, "languageVersion", languageVersion, forTests)
}
@@ -327,12 +394,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return kotlinBlock.parent
}
private fun changeBuildGradle(module: Module, body: (GroovyFile) -> PsiElement?): PsiElement? {
val gradleFilePath = getModuleFilePath(module)
val gradleFile = getBuildGradleFile(module.project, gradleFilePath)
if (gradleFile != null && canConfigureFile(gradleFile)) {
return gradleFile.project.executeWriteCommand("Change build.gradle configuration", null) {
body(gradleFile)
private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
val buildScriptFile = module.getBuildScriptPsiFile()
if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
return buildScriptFile.project.executeWriteCommand("Change build.gradle configuration", null) {
body(buildScriptFile)
}
}
return null
@@ -353,9 +419,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
}
fun getKotlinStdlibVersion(module: Module): String? {
val gradleFilePath = getModuleFilePath(module)
val gradleFile = getBuildGradleFile(module.project, gradleFilePath) ?: return null
val gradleFile = module.getBuildScriptPsiFile() as? GroovyFile ?: return null
val versionProperty = "\$kotlin_version"
val block = getBuildScriptBlock(gradleFile)
if (block.text.contains("ext.kotlin_version = ")) {
@@ -376,7 +440,28 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return null
}
fun addElementsToProjectFile(file: GroovyFile, version: String): Boolean {
fun addElementsToProjectFile(file: PsiFile, version: String): Boolean = when (file) {
is GroovyFile -> addElementsToProjectGroovyFile(file, version)
is KtFile -> addElementsToProjectGSKFile(file, version)
else -> error("Unknown build script file type")
}
fun addElementsToProjectGSKFile(file: KtFile, version: String): Boolean {
val originalText = file.text
file.getBuildScriptBlock()?.apply {
addDeclarationIfMissing("var $GSK_KOTLIN_VERSION_PROPERTY_NAME: String by extra", true).also {
addExpressionAfterIfMissing("$GSK_KOTLIN_VERSION_PROPERTY_NAME = \"$version\"", it)
}
getRepositoriesBlock()?.addRepositoryIfMissing(version)
getDependenciesBlock()?.addPluginToClassPathIfMissing()
}
return originalText != file.text
}
fun addElementsToProjectGroovyFile(file: GroovyFile, version: String): Boolean {
var wasModified: Boolean
val buildScriptBlock = getBuildScriptBlock(file)
@@ -391,53 +476,47 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return wasModified
}
private fun isRepositoryConfigured(repositoriesBlock: GrClosableBlock): Boolean {
return repositoriesBlock.text.contains(MAVEN_CENTRAL) || repositoriesBlock.text.contains(JCENTER)
private fun isRepositoryConfigured(repositoriesBlockText: String): Boolean {
return repositoriesBlockText.contains(MAVEN_CENTRAL) || repositoriesBlockText.contains(JCENTER)
}
private fun canConfigureFile(file: GroovyFile): Boolean {
private fun canConfigureFile(file: PsiFile): Boolean {
return WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
}
private fun getBuildGradleFile(project: Project, path: String?): GroovyFile? {
if (path == null) {
return null
}
val file = VfsUtil.findFileByIoFile(File(path), true) ?: return null
val psiFile = PsiManager.getInstance(project).findFile(file) as? GroovyFile ?: return null
return psiFile
}
private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project)
private fun getTopLevelProjectFilePath(project: Project): String {
return project.basePath + "/" + GradleConstants.DEFAULT_SCRIPT_NAME
}
private fun Project.getTopLevelBuildScriptPsiFile() = basePath?.let { findBuildGradleFile(it)?.getPsiFile(this) }
private fun getModuleFilePath(module: Module): String? {
val moduleDir = File(module.moduleFilePath).parent
var buildGradleFile = File(moduleDir + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)
if (buildGradleFile.exists()) {
return buildGradleFile.path
private fun Module.getBuildScriptFile(): File? {
val moduleDir = File(moduleFilePath).parent
findBuildGradleFile(moduleDir)?.let {
return it
}
// since IDEA 145 module file is located in .idea directory
for (file in ModuleRootManager.getInstance(module).contentRoots) {
buildGradleFile = File(file.path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)
if (buildGradleFile.exists()) {
return buildGradleFile.path
ModuleRootManager.getInstance(this).contentRoots.forEach { root ->
findBuildGradleFile(root.path)?.let {
return it
}
}
val externalProjectPath = ExternalSystemApiUtil.getExternalProjectPath(module)
if (externalProjectPath != null) {
buildGradleFile = File(externalProjectPath + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)
if (buildGradleFile.exists()) {
return buildGradleFile.path
ExternalSystemApiUtil.getExternalProjectPath(this)?.let { externalProjectPath ->
findBuildGradleFile(externalProjectPath)?.let {
return it
}
}
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 File.getPsiFile(project: Project) = VfsUtil.findFileByIoFile(this, true)?.let {
PsiManager.getInstance(project).findFile(it)
}
private fun getDependenciesBlock(file: GrStatementOwner): GrClosableBlock {
return getBlockOrCreate(file, "dependencies")
}
@@ -456,7 +535,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
private fun getRepositoriesBlock(file: GrStatementOwner) = getBlockOrCreate(file, "repositories")
fun getBlockOrCreate(parent: GrStatementOwner, name: String): GrClosableBlock {
private fun getBlockOrCreate(parent: GrStatementOwner, name: String): GrClosableBlock {
var block = getBlockByName(parent, name)
if (block == null) {
val factory = GroovyPsiElementFactory.getInstance(parent.project)
@@ -467,7 +546,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
return block
}
fun addLastExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
private fun addLastExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
return addExpressionInBlockIfNeeded(text, block, false)
}
@@ -517,17 +596,11 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
private fun addRepository(repositoriesBlock: GrClosableBlock, version: String): Boolean {
val repository = getRepositoryForVersion(version)
val snippet = when {
repository != null -> repository.toRepositorySnippet()
!isRepositoryConfigured(repositoriesBlock) -> MAVEN_CENTRAL
repository != null -> repository.toGroovyRepositorySnippet()
!isRepositoryConfigured(repositoriesBlock.text) -> "$MAVEN_CENTRAL\n"
else -> return false
}
return addLastExpressionInBlockIfNeeded(snippet, repositoriesBlock)
}
fun getRuntimeLibraryForSdk(sdk: Sdk?, version: String): String {
return getDependencySnippet(getStdlibArtifactId(sdk, version))
}
fun getDependencySnippet(artifactId: String) = "compile \"org.jetbrains.kotlin:$artifactId:\$kotlin_version\""
}
}
@@ -66,7 +66,7 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
val showPathToJarPanel = needToChooseJarPath(project)
var nonConfiguredModules = if (!ApplicationManager.getApplication().isUnitTestMode)
getNonConfiguredModules(project, this)
getCanBeConfiguredModules(project, this)
else
Arrays.asList(*ModuleManager.getInstance(project).modules)
nonConfiguredModules -= excludeModules
@@ -24,7 +24,7 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator
import org.jetbrains.kotlin.idea.configuration.getAbleToRunConfigurators
import org.jetbrains.kotlin.idea.configuration.getConfiguratorByName
import org.jetbrains.kotlin.idea.configuration.getNonConfiguredModulesWithKotlinFiles
import org.jetbrains.kotlin.idea.configuration.getCanBeConfiguredModulesWithKotlinFiles
import org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent
import javax.swing.event.HyperlinkEvent
@@ -57,7 +57,7 @@ class ConfigureKotlinNotification(
companion object {
fun getNotificationString(project: Project, excludeModules: Collection<Module>): String? {
val modules = getNonConfiguredModulesWithKotlinFiles(project, excludeModules)
val modules = getCanBeConfiguredModulesWithKotlinFiles(project, excludeModules)
val isOnlyOneModule = modules.size == 1
@@ -52,8 +52,8 @@ public class ChooseModulePanel {
public ChooseModulePanel(@NotNull Project project, @NotNull KotlinProjectConfigurator configurator, Collection<Module> excludeModules) {
this.project = project;
this.modules = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModules(project, configurator);
this.modulesWithKtFiles = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModulesWithKotlinFiles(project, configurator);
this.modules = ConfigureKotlinInProjectUtilsKt.getCanBeConfiguredModules(project, configurator);
this.modulesWithKtFiles = ConfigureKotlinInProjectUtilsKt.getCanBeConfiguredModulesWithKotlinFiles(project, configurator);
DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel();