Refactor Kotlin Gradle configurator
This commit is contained in:
+17
-20
@@ -17,17 +17,18 @@
|
||||
package org.jetbrains.kotlin.android.configure
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.projectRoots.JavaSdk
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator
|
||||
import org.jetbrains.kotlin.idea.versions.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.idea.versions.MAVEN_STDLIB_ID_JRE7
|
||||
import org.jetbrains.kotlin.idea.versions.hasJreSpecificRuntime
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
||||
|
||||
class KotlinAndroidGradleModuleConfigurator internal constructor() : KotlinWithGradleConfigurator() {
|
||||
|
||||
@@ -42,25 +43,21 @@ class KotlinAndroidGradleModuleConfigurator internal constructor() : KotlinWithG
|
||||
override val kotlinPluginName: String = KOTLIN_ANDROID
|
||||
|
||||
override fun addElementsToFile(file: PsiFile, isTopLevelProjectFile: Boolean, version: String): Boolean {
|
||||
if (file is GroovyFile) {
|
||||
if (isTopLevelProjectFile) {
|
||||
return addElementsToProjectGroovyFile(file, version)
|
||||
}
|
||||
else {
|
||||
return addElementsToModuleGroovyFile(file, version)
|
||||
}
|
||||
}
|
||||
val manipulator = getManipulator(file)
|
||||
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
|
||||
val jvmTarget = getJvmTarget(sdk, version)
|
||||
|
||||
if (file is KtFile) {
|
||||
if (isTopLevelProjectFile) {
|
||||
return addElementsToProjectGSKFile(file, version)
|
||||
}
|
||||
else {
|
||||
return addElementsToModuleGSKFile(file, version)
|
||||
}
|
||||
return if (isTopLevelProjectFile) {
|
||||
manipulator.configureProjectBuildScript(version)
|
||||
}
|
||||
else {
|
||||
manipulator.configureModuleBuildScript(
|
||||
kotlinPluginName,
|
||||
getStdlibArtifactName(sdk, version),
|
||||
version,
|
||||
jvmTarget
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getStdlibArtifactName(sdk: Sdk?, version: String): String {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.psi.search.FileTypeIndex
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
@@ -65,6 +66,24 @@ val EAP_12_REPOSITORY = RepositoryDescription(
|
||||
"https://bintray.com/kotlin/kotlin-eap-1.2/kotlin/",
|
||||
isSnapshot = false)
|
||||
|
||||
val MAVEN_CENTRAL = "mavenCentral()"
|
||||
|
||||
val JCENTER = "jcenter()"
|
||||
|
||||
val KOTLIN_GROUP_ID = "org.jetbrains.kotlin"
|
||||
|
||||
fun isRepositoryConfigured(repositoriesBlockText: String): Boolean =
|
||||
repositoriesBlockText.contains(MAVEN_CENTRAL) || repositoriesBlockText.contains(JCENTER)
|
||||
|
||||
fun DependencyScope.toGradleCompileScope(isAndroidModule: Boolean) = when (this) {
|
||||
DependencyScope.COMPILE -> "compile"
|
||||
// TODO: We should add testCompile or androidTestCompile
|
||||
DependencyScope.TEST -> if (isAndroidModule) "compile" else "testCompile"
|
||||
DependencyScope.RUNTIME -> "runtime"
|
||||
DependencyScope.PROVIDED -> "compile"
|
||||
else -> "compile"
|
||||
}
|
||||
|
||||
fun RepositoryDescription.toGroovyRepositorySnippet() = "maven {\nurl '$url'\n}"
|
||||
|
||||
fun RepositoryDescription.toKotlinRepositorySnippet() = "maven {\nsetUrl(\"$url\")\n}"
|
||||
@@ -240,4 +259,4 @@ fun isEap(version: String): Boolean {
|
||||
fun useEapRepository(minorKotlinVersion: Int, version: String): Boolean {
|
||||
return Regex("1\\.$minorKotlinVersion(\\.\\d)?-[A-Za-z][A-Za-z0-9-]*").matches(version) &&
|
||||
!version.startsWith("1.$minorKotlinVersion.0-dev")
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* 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 getGSKCompileDependencySnippet(groupId: String, artifactId: String, compileScope: String = "compile", version: String? = null): String =
|
||||
if (groupId == KOTLIN_GROUP_ID)
|
||||
"$compileScope(${getKotlinModuleDependencySnippet(artifactId, version)})"
|
||||
else
|
||||
"$compileScope(\"$groupId:$artifactId:$version\")"
|
||||
|
||||
fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String =
|
||||
"kotlinModule(\"${artifactId.removePrefix("kotlin-")}\", ${version?.let { "\"$it\"" } ?: 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 KtFile.getKotlinStdlibVersion(): String? {
|
||||
return findScriptInitializer("dependencies")?.getBlock()?.let {
|
||||
val expression = it.findCompileStdLib()?.valueArguments?.firstOrNull()?.getArgumentExpression()
|
||||
when (expression) {
|
||||
is KtCallExpression -> expression.valueArguments[1].text.trim('\"')
|
||||
is KtStringTemplateExpression -> expression.text?.trim('\"')?.substringAfterLast(":")?.removePrefix("$")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KtBlockExpression.findCompileStdLib(): KtCallExpression? {
|
||||
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
|
||||
it.calleeExpression?.text == "compile" && (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 KtStringTemplateExpression -> text.startsWith("\"$STDLIB_ARTIFACT_PREFIX")
|
||||
else -> 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 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()"
|
||||
private val STDLIB_ARTIFACT_PREFIX = "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
|
||||
val KOTLIN_GROUP_ID = "org.jetbrains.kotlin"
|
||||
val GSK_KOTLIN_VERSION_PROPERTY_NAME = "kotlin_version"
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
interface GradleBuildScriptManipulator {
|
||||
fun isConfigured(kotlinPluginName: String): Boolean
|
||||
|
||||
fun configureModuleBuildScript(kotlinPluginName: String,
|
||||
stdlibArtifactName: String,
|
||||
version: String,
|
||||
jvmTarget: String?): Boolean
|
||||
|
||||
fun configureProjectBuildScript(version: String): Boolean
|
||||
|
||||
fun changeCoroutineConfiguration(coroutineOption: String): PsiElement?
|
||||
|
||||
fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement?
|
||||
|
||||
fun changeApiVersion(version: String, forTests: Boolean): PsiElement?
|
||||
|
||||
fun addKotlinLibraryToModuleBuildScript(
|
||||
scope: DependencyScope,
|
||||
libraryDescriptor: ExternalLibraryDescriptor,
|
||||
isAndroidModule: Boolean)
|
||||
|
||||
fun getKotlinStdlibVersion(): String?
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
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
|
||||
|
||||
class GroovyBuildScriptManipulator(private val groovyScript: GroovyFile) : GradleBuildScriptManipulator {
|
||||
override fun isConfigured(kotlinPluginName: String): Boolean {
|
||||
val fileText = groovyScript.text
|
||||
return containsDirective(fileText, getApplyPluginDirective(kotlinPluginName)) &&
|
||||
fileText.contains("org.jetbrains.kotlin") &&
|
||||
fileText.contains("kotlin-stdlib")
|
||||
}
|
||||
|
||||
override fun configureModuleBuildScript(
|
||||
kotlinPluginName: 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 buildScriptBlock = groovyScript.getBlockByName("buildscript")
|
||||
if (buildScriptBlock != null) {
|
||||
groovyScript.addAfter(apply, buildScriptBlock.parent)
|
||||
}
|
||||
else {
|
||||
groovyScript.addAfter(apply, groovyScript.statements.lastOrNull() ?: groovyScript.firstChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groovyScript.getRepositoriesBlock().apply {
|
||||
addRepository(version)
|
||||
}
|
||||
|
||||
groovyScript.getDependenciesBlock().apply {
|
||||
addExpressionInBlockIfNeeded(getGroovyDependencySnippet(stdlibArtifactName), false)
|
||||
}
|
||||
|
||||
if (jvmTarget != null) {
|
||||
changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = false)
|
||||
changeKotlinTaskParameter(groovyScript, "jvmTarget", jvmTarget, forTests = true)
|
||||
}
|
||||
|
||||
return groovyScript.text != oldText
|
||||
}
|
||||
|
||||
override fun configureProjectBuildScript(version: String): Boolean {
|
||||
val oldText = groovyScript.text
|
||||
groovyScript.apply {
|
||||
getBuildScriptBlock().apply {
|
||||
addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version))
|
||||
}
|
||||
|
||||
getBuildScriptRepositoriesBlock().apply {
|
||||
addRepository(version)
|
||||
}
|
||||
|
||||
getBuildScriptDependenciesBlock().apply {
|
||||
addLastExpressionInBlockIfNeeded(CLASSPATH)
|
||||
}
|
||||
}
|
||||
|
||||
return oldText != groovyScript.text
|
||||
}
|
||||
|
||||
override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? {
|
||||
val snippet = "coroutines \"$coroutineOption\""
|
||||
val kotlinBlock = groovyScript.getBlockOrCreate("kotlin")
|
||||
kotlinBlock.getBlockOrCreate("experimental").apply {
|
||||
addOrReplaceExpression(snippet) { stmt ->
|
||||
(stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines"
|
||||
}
|
||||
}
|
||||
|
||||
return kotlinBlock.parent
|
||||
}
|
||||
|
||||
override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? =
|
||||
changeKotlinTaskParameter(groovyScript, "languageVersion", version, forTests)
|
||||
|
||||
override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? =
|
||||
changeKotlinTaskParameter(groovyScript, "apiVersion", version, forTests)
|
||||
|
||||
override fun addKotlinLibraryToModuleBuildScript(
|
||||
scope: DependencyScope,
|
||||
libraryDescriptor: ExternalLibraryDescriptor,
|
||||
isAndroidModule: Boolean
|
||||
) {
|
||||
val dependencyString = String.format(
|
||||
"%s \"%s:%s:%s\"",
|
||||
scope.toGradleCompileScope(isAndroidModule),
|
||||
libraryDescriptor.libraryGroupId,
|
||||
libraryDescriptor.libraryArtifactId,
|
||||
libraryDescriptor.maxVersion)
|
||||
|
||||
groovyScript.getDependenciesBlock().apply {
|
||||
addLastExpressionInBlockIfNeeded(dependencyString)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKotlinStdlibVersion(): String? {
|
||||
val versionProperty = "\$kotlin_version"
|
||||
groovyScript.getBlockByName("buildScript")?.let {
|
||||
if (it.text.contains("ext.kotlin_version = ")) {
|
||||
return versionProperty
|
||||
}
|
||||
}
|
||||
|
||||
val dependencies = groovyScript.getBlockByName("dependencies")?.statements
|
||||
val stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:"
|
||||
dependencies?.forEach { dependency ->
|
||||
val dependencyText = dependency.text
|
||||
val startIndex = dependencyText.indexOf(stdlibArtifactPrefix) + stdlibArtifactPrefix.length
|
||||
val endIndex = dependencyText.length - 1
|
||||
if (startIndex != -1 && endIndex != -1) {
|
||||
return dependencyText.substring(startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun changeKotlinTaskParameter(
|
||||
gradleFile: GroovyFile,
|
||||
parameterName: String,
|
||||
parameterValue: String,
|
||||
forTests: Boolean
|
||||
): PsiElement? {
|
||||
val snippet = "$parameterName = \"$parameterValue\""
|
||||
val kotlinBlock = gradleFile.getBlockOrCreate(if (forTests) "compileTestKotlin" else "compileKotlin")
|
||||
|
||||
for (stmt in kotlinBlock.statements) {
|
||||
if ((stmt as? GrAssignmentExpression)?.lValue?.text == "kotlinOptions." + parameterName) {
|
||||
return stmt.replaceWithStatementFromText("kotlinOptions." + snippet)
|
||||
}
|
||||
}
|
||||
|
||||
kotlinBlock.getBlockOrCreate("kotlinOptions").apply {
|
||||
addOrReplaceExpression(snippet) { stmt ->
|
||||
(stmt as? GrAssignmentExpression)?.lValue?.text == parameterName
|
||||
}
|
||||
}
|
||||
|
||||
return kotlinBlock.parent
|
||||
}
|
||||
|
||||
private fun getGroovyDependencySnippet(artifactName: String) = "compile \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
|
||||
|
||||
private fun getApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
||||
|
||||
private fun containsDirective(fileText: String, directive: String): Boolean {
|
||||
return fileText.contains(directive)
|
||||
|| fileText.contains(directive.replace("\"", "'"))
|
||||
|| fileText.contains(directive.replace("'", "\""))
|
||||
}
|
||||
|
||||
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 {
|
||||
repository != null -> repository.toGroovyRepositorySnippet()
|
||||
!isRepositoryConfigured(text) -> "$MAVEN_CENTRAL\n"
|
||||
else -> return false
|
||||
}
|
||||
return addLastExpressionInBlockIfNeeded(snippet)
|
||||
}
|
||||
|
||||
private fun GrStatementOwner.getRepositoriesBlock() = getBlockOrCreate("repositories")
|
||||
|
||||
private fun GrStatementOwner.getDependenciesBlock(): GrClosableBlock = getBlockOrCreate("dependencies")
|
||||
|
||||
private fun GrStatementOwner.getBuildScriptBlock() = getBlockOrCreate("buildscript")
|
||||
|
||||
private fun GrStatementOwner.getBuildScriptRepositoriesBlock(): GrClosableBlock =
|
||||
getBuildScriptBlock().getBlockOrCreate("repositories")
|
||||
|
||||
private fun GrStatementOwner.getBuildScriptDependenciesBlock(): GrClosableBlock =
|
||||
getBuildScriptBlock().getBlockOrCreate("dependencies")
|
||||
|
||||
private fun GrStatementOwner.getBlockOrCreate(name: String): GrClosableBlock {
|
||||
var block = getBlockByName(name)
|
||||
if (block == null) {
|
||||
val factory = GroovyPsiElementFactory.getInstance(project)
|
||||
val newBlock = factory.createExpressionFromText("$name{\n}\n")
|
||||
addAfter(newBlock, statements.lastOrNull() ?: firstChild)
|
||||
block = getBlockByName(name)!!
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
private fun GrClosableBlock.addOrReplaceExpression(snippet: String, predicate: (GrStatement) -> Boolean) {
|
||||
statements.firstOrNull(predicate)?.let { stmt ->
|
||||
stmt.replaceWithStatementFromText(snippet)
|
||||
return
|
||||
}
|
||||
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 {
|
||||
val newStatement = GroovyPsiElementFactory.getInstance(project).createExpressionFromText(snippet)
|
||||
CodeStyleManager.getInstance(project).reformat(newStatement)
|
||||
return replaceWithStatement(newStatement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VERSION_TEMPLATE = "\$VERSION$"
|
||||
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\""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
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
|
||||
|
||||
class KotlinBuildScriptManipulator(private val kotlinScript: KtFile) : GradleBuildScriptManipulator {
|
||||
override fun isConfigured(kotlinPluginName: String): Boolean =
|
||||
kotlinScript.containsApplyKotlinPlugin(kotlinPluginName) && kotlinScript.containsCompileStdLib()
|
||||
|
||||
override fun configureProjectBuildScript(version: String): Boolean {
|
||||
val originalText = kotlinScript.text
|
||||
kotlinScript.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 != kotlinScript.text
|
||||
}
|
||||
|
||||
override fun configureModuleBuildScript(
|
||||
kotlinPluginName: 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()?.addRepositoryIfMissing(version)
|
||||
jvmTarget?.let {
|
||||
changeKotlinTaskParameter("jvmTarget", it, forTests = false)
|
||||
changeKotlinTaskParameter("jvmTarget", it, forTests = true)
|
||||
}
|
||||
}
|
||||
|
||||
return originalText != kotlinScript.text
|
||||
}
|
||||
|
||||
override fun changeCoroutineConfiguration(coroutineOption: String): PsiElement? =
|
||||
kotlinScript.changeCoroutineConfiguration(coroutineOption)
|
||||
|
||||
override fun changeLanguageVersion(version: String, forTests: Boolean): PsiElement? =
|
||||
kotlinScript.changeKotlinTaskParameter("languageVersion", version, forTests)
|
||||
|
||||
override fun changeApiVersion(version: String, forTests: Boolean): PsiElement? =
|
||||
kotlinScript.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 {
|
||||
addExpressionIfMissing(
|
||||
getCompileDependencySnippet(
|
||||
libraryDescriptor.libraryGroupId,
|
||||
libraryDescriptor.libraryArtifactId,
|
||||
scope.toGradleCompileScope(isAndroidModule),
|
||||
kotlinLibraryVersion))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKotlinStdlibVersion(): String? = kotlinScript.getKotlinStdlibVersion()
|
||||
|
||||
private fun KtBlockExpression.addCompileStdlibIfMissing(stdlibArtifactName: String): KtCallExpression? =
|
||||
findCompileStdLib() ?: addExpressionIfMissing(getCompileDependencySnippet(KOTLIN_GROUP_ID, stdlibArtifactName)) as? KtCallExpression
|
||||
|
||||
private fun KtFile.containsCompileStdLib(): Boolean =
|
||||
findScriptInitializer("dependencies")?.getBlock()?.findCompileStdLib() != null
|
||||
|
||||
private fun KtFile.containsApplyKotlinPlugin(pluginName: String): Boolean =
|
||||
findScriptInitializer("apply")?.getBlock()?.findPlugin(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 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 getCompileDependencySnippet(groupId: String, artifactId: String, compileScope: String = "compile", version: String? = null): String =
|
||||
if (groupId == KOTLIN_GROUP_ID)
|
||||
"$compileScope(${getKotlinModuleDependencySnippet(artifactId, version)})"
|
||||
else
|
||||
"$compileScope(\"$groupId:$artifactId:$version\")"
|
||||
|
||||
private fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String =
|
||||
"kotlinModule(\"${artifactId.removePrefix("kotlin-")}\", ${version?.let { "\"$it\"" } ?: GSK_KOTLIN_VERSION_PROPERTY_NAME})"
|
||||
|
||||
private fun KtFile.getKotlinStdlibVersion(): String? {
|
||||
return findScriptInitializer("dependencies")?.getBlock()?.let {
|
||||
val expression = it.findCompileStdLib()?.valueArguments?.firstOrNull()?.getArgumentExpression()
|
||||
when (expression) {
|
||||
is KtCallExpression -> expression.valueArguments[1].text.trim('\"')
|
||||
is KtStringTemplateExpression -> expression.text?.trim('\"')?.substringAfterLast(":")?.removePrefix("$")
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBlockExpression.findCompileStdLib(): KtCallExpression? {
|
||||
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
|
||||
it.calleeExpression?.text == "compile" && (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 KtStringTemplateExpression -> text.startsWith("\"$STDLIB_ARTIFACT_PREFIX")
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun KtFile.getRepositoriesBlock(): KtBlockExpression? =
|
||||
findScriptInitializer("repositories")?.getBlock() ?: addTopLevelBlock("repositories")
|
||||
|
||||
private fun KtFile.getDependenciesBlock(): KtBlockExpression? =
|
||||
findScriptInitializer("dependencies")?.getBlock() ?: addTopLevelBlock("dependencies")
|
||||
|
||||
private 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()
|
||||
}
|
||||
|
||||
private fun KtFile.getApplyBlock(): KtBlockExpression? = findScriptInitializer("apply")?.getBlock() ?: createApplyBlock()
|
||||
|
||||
private fun KtBlockExpression.createPluginIfMissing(pluginName: String): KtCallExpression? =
|
||||
findPlugin(pluginName) ?: addExpressionIfMissing("plugin(\"$pluginName\")") as? KtCallExpression
|
||||
|
||||
private fun KtFile.changeCoroutineConfiguration(coroutineOption: String): PsiElement? {
|
||||
val snippet = "experimental.coroutines = Coroutines.${coroutineOption.toUpperCase()}"
|
||||
val kotlinBlock = findScriptInitializer("kotlin")?.getBlock() ?: addTopLevelBlock("kotlin") ?: return null
|
||||
addImportIfMissing("org.jetbrains.kotlin.gradle.dsl.Coroutines")
|
||||
val statement = kotlinBlock.statements.find { it.text.startsWith("experimental.coroutines") }
|
||||
return if (statement != null) {
|
||||
statement.replace(psiFactory.createExpression(snippet))
|
||||
}
|
||||
else {
|
||||
kotlinBlock.add(psiFactory.createExpression(snippet)).apply { addNewLinesIfNeeded() }
|
||||
}
|
||||
}
|
||||
|
||||
private 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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtFile.getBuildScriptBlock(): KtBlockExpression? =
|
||||
findScriptInitializer("buildscript")?.getBlock() ?: addTopLevelBlock("buildscript", true)
|
||||
|
||||
private fun KtBlockExpression.getRepositoriesBlock(): KtBlockExpression? =
|
||||
findBlock("repositories") ?: addBlock("repositories")
|
||||
|
||||
private fun KtBlockExpression.getDependenciesBlock(): KtBlockExpression? =
|
||||
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
|
||||
}
|
||||
|
||||
return addExpressionIfMissing(snippet) as? KtCallExpression
|
||||
}
|
||||
|
||||
private fun KtBlockExpression.addPluginToClassPathIfMissing(): KtCallExpression? =
|
||||
addExpressionIfMissing("classpath(${getKotlinModuleDependencySnippet("gradle-plugin")})") as? KtCallExpression
|
||||
|
||||
|
||||
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
|
||||
|
||||
private fun KtBlockExpression.addExpressionAfterIfMissing(text: String, after: PsiElement): KtExpression = addStatementIfMissing(text) {
|
||||
psiFactory.createExpression(it).let { created ->
|
||||
addAfter(created, after)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBlockExpression.addExpressionIfMissing(text: String, first: Boolean = false): KtExpression = addStatementIfMissing(text) {
|
||||
psiFactory.createExpression(it).let { created ->
|
||||
if(first) addAfter(created, null) else add(created)
|
||||
}
|
||||
}
|
||||
|
||||
private 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)
|
||||
|
||||
companion object {
|
||||
private val STDLIB_ARTIFACT_PREFIX = "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
private val GSK_KOTLIN_VERSION_PROPERTY_NAME = "kotlin_version"
|
||||
}
|
||||
}
|
||||
@@ -33,26 +33,14 @@ 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
|
||||
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
|
||||
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
|
||||
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 java.io.File
|
||||
import java.util.*
|
||||
|
||||
@@ -80,32 +68,18 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
return ConfigureKotlinStatus.BROKEN
|
||||
}
|
||||
|
||||
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)
|
||||
private fun PsiFile.isConfiguredByAnyGradleConfigurator(): Boolean {
|
||||
return Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
||||
.filterIsInstance<KotlinWithGradleConfigurator>()
|
||||
.any { it.isFileConfigured(this) }
|
||||
}
|
||||
|
||||
protected open fun isApplicable(module: Module): Boolean =
|
||||
KotlinPluginUtil.isGradleModule(module) && !KotlinPluginUtil.isAndroidGradleModule(module)
|
||||
|
||||
protected open fun getMinimumSupportedVersion() = "1.0.0"
|
||||
|
||||
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")
|
||||
}
|
||||
private fun isFileConfigured(buildScript: PsiFile): Boolean = getManipulator(buildScript).isConfigured(kotlinPluginName)
|
||||
|
||||
@JvmSuppressWildcards
|
||||
override fun configure(project: Project, excludeModules: Collection<Module>) {
|
||||
@@ -132,7 +106,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
val changedFiles = HashSet<PsiFile>()
|
||||
val buildScript = project.getTopLevelBuildScriptPsiFile()
|
||||
if (buildScript != null && canConfigureFile(buildScript)) {
|
||||
val isModified = changeBuildScript(buildScript, true, kotlinVersion, collector)
|
||||
val isModified = configureBuildScript(buildScript, true, kotlinVersion, collector)
|
||||
if (isModified) {
|
||||
changedFiles.add(buildScript)
|
||||
}
|
||||
@@ -141,7 +115,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
for (module in modulesToConfigure) {
|
||||
val file = module.getBuildScriptPsiFile()
|
||||
if (file != null && canConfigureFile(file)) {
|
||||
val isModified = changeBuildScript(file, false, kotlinVersion, collector)
|
||||
val isModified = configureBuildScript(file, false, kotlinVersion, collector)
|
||||
if (isModified) {
|
||||
changedFiles.add(file)
|
||||
}
|
||||
@@ -153,71 +127,18 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
return changedFiles
|
||||
}
|
||||
|
||||
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
|
||||
protected fun configureModuleBuildScript(file: PsiFile, version: String): Boolean {
|
||||
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(getGSKCompileDependencySnippet(KOTLIN_GROUP_ID, 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)
|
||||
if (applyStatement != null) {
|
||||
file.addAfter(apply, applyStatement)
|
||||
}
|
||||
else {
|
||||
val buildScript = getBlockByName(file, "buildscript")
|
||||
if (buildScript != null) {
|
||||
file.addAfter(apply, buildScript.parent)
|
||||
}
|
||||
else {
|
||||
file.addAfter(apply, file.statements.lastOrNull() ?: file.firstChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val repositoriesBlock = getRepositoriesBlock(file)
|
||||
addRepository(repositoriesBlock, version)
|
||||
|
||||
val dependenciesBlock = getDependenciesBlock(file)
|
||||
val sdk = ModuleUtil.findModuleForPsiElement(file)?.let { ModuleRootManager.getInstance(it).sdk }
|
||||
addExpressionInBlockIfNeeded(getStdlibDependencyDirectiveForGroovy(sdk, version), dependenciesBlock, false)
|
||||
val jvmTarget = getJvmTarget(sdk, version)
|
||||
if (jvmTarget != null) {
|
||||
changeKotlinTaskParameter(file, "jvmTarget", jvmTarget, forTests = false)
|
||||
changeKotlinTaskParameter(file, "jvmTarget", jvmTarget, forTests = true)
|
||||
}
|
||||
|
||||
return file.text != oldText
|
||||
return getManipulator(file).configureModuleBuildScript(
|
||||
kotlinPluginName,
|
||||
getStdlibArtifactName(sdk, version),
|
||||
version,
|
||||
jvmTarget)
|
||||
}
|
||||
|
||||
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 kotlinPluginName: String
|
||||
@@ -228,14 +149,14 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
version: String
|
||||
): Boolean {
|
||||
if (!isTopLevelProjectFile) {
|
||||
var wasModified = addElementsToProjectFile(file, version)
|
||||
wasModified = wasModified or addElementsToModuleFile(file, version)
|
||||
var wasModified = configureProjectFile(file, version)
|
||||
wasModified = wasModified or configureModuleBuildScript(file, version)
|
||||
return wasModified
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun changeBuildScript(
|
||||
fun configureBuildScript(
|
||||
file: PsiFile,
|
||||
isTopLevelProjectFile: Boolean,
|
||||
version: String,
|
||||
@@ -256,26 +177,19 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VERSION_TEMPLATE = "\$VERSION$"
|
||||
fun getManipulator(file: PsiFile): GradleBuildScriptManipulator = when (file) {
|
||||
is KtFile -> KotlinBuildScriptManipulator(file)
|
||||
is GroovyFile -> GroovyBuildScriptManipulator(file)
|
||||
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 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 getGroovyDependencySnippet(artifactName: String) = "compile \"org.jetbrains.kotlin:$artifactName:\$kotlin_version\""
|
||||
|
||||
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
||||
@@ -286,11 +200,8 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
return
|
||||
}
|
||||
|
||||
val isAndroidModule = KotlinPluginUtil.isAndroidGradleModule(module)
|
||||
when (buildScript) {
|
||||
is GroovyFile -> addKotlinLibraryToModuleGroovyFile(buildScript, scope, libraryDescriptor, isAndroidModule)
|
||||
is KtFile -> addKotlinLibraryToModuleGSKFile(buildScript, scope, libraryDescriptor, isAndroidModule)
|
||||
}
|
||||
getManipulator(buildScript)
|
||||
.addKotlinLibraryToModuleBuildScript(scope, libraryDescriptor, KotlinPluginUtil.isAndroidGradleModule(module))
|
||||
|
||||
buildScript.virtualFile?.let {
|
||||
createConfigureKotlinNotificationCollector(buildScript.project)
|
||||
@@ -299,131 +210,26 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
}
|
||||
}
|
||||
|
||||
fun addKotlinLibraryToModuleGroovyFile(
|
||||
gradleFile: GroovyFile,
|
||||
scope: DependencyScope,
|
||||
libraryDescriptor: ExternalLibraryDescriptor,
|
||||
isAndroidModule: Boolean) {
|
||||
gradleFile.project.executeWriteCommand("Add Kotlin library") {
|
||||
val dependencyString = String.format(
|
||||
"%s \"%s:%s:%s\"",
|
||||
scope.toGradleCompileScope(isAndroidModule),
|
||||
libraryDescriptor.libraryGroupId,
|
||||
libraryDescriptor.libraryArtifactId,
|
||||
libraryDescriptor.maxVersion)
|
||||
|
||||
val dependenciesBlock = getDependenciesBlock(gradleFile)
|
||||
addLastExpressionInBlockIfNeeded(dependencyString, dependenciesBlock)
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(gradleFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun addKotlinLibraryToModuleGSKFile(
|
||||
gradleFile: KtFile,
|
||||
scope: DependencyScope,
|
||||
libraryDescriptor: ExternalLibraryDescriptor,
|
||||
isAndroidModule: Boolean) {
|
||||
gradleFile.project.executeWriteCommand("Add Kotlin library") {
|
||||
val kotlinLibraryVersion = libraryDescriptor.maxVersion.takeIf { it != GSK_KOTLIN_VERSION_PROPERTY_NAME }
|
||||
gradleFile.getDependenciesBlock()?.apply {
|
||||
addExpressionIfMissing(
|
||||
getGSKCompileDependencySnippet(
|
||||
libraryDescriptor.libraryGroupId,
|
||||
libraryDescriptor.libraryArtifactId,
|
||||
scope.toGradleCompileScope(isAndroidModule),
|
||||
kotlinLibraryVersion))
|
||||
}
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(gradleFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DependencyScope.toGradleCompileScope(isAndroidModule: Boolean) = when (this) {
|
||||
DependencyScope.COMPILE -> "compile"
|
||||
// TODO we should add testCompile or androidTestCompile
|
||||
DependencyScope.TEST -> if (isAndroidModule) "compile" else "testCompile"
|
||||
DependencyScope.RUNTIME -> "runtime"
|
||||
DependencyScope.PROVIDED -> "compile"
|
||||
else -> "compile"
|
||||
}
|
||||
|
||||
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? {
|
||||
return changeBuildGradle(module) { buildScriptFile ->
|
||||
when (buildScriptFile) {
|
||||
is GroovyFile -> changeCoroutineConfiguration(buildScriptFile, coroutineOption)
|
||||
is KtFile -> changeCoroutineConfiguration(buildScriptFile, coroutineOption)
|
||||
else -> error("Unknown build script file type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCoroutineConfiguration(gradleFile: GroovyFile, coroutineOption: String): PsiElement? {
|
||||
val snippet = "coroutines \"$coroutineOption\""
|
||||
val kotlinBlock = getBlockOrCreate(gradleFile, "kotlin")
|
||||
val experimentalBlock = getBlockOrCreate(kotlinBlock, "experimental")
|
||||
addOrReplaceExpression(experimentalBlock, snippet) { stmt ->
|
||||
(stmt as? GrMethodCall)?.invokedExpression?.text == "coroutines"
|
||||
}
|
||||
return kotlinBlock.parent
|
||||
fun changeCoroutineConfiguration(module: Module, coroutineOption: String): PsiElement? = changeBuildGradle(module) {
|
||||
getManipulator(it).changeCoroutineConfiguration(coroutineOption)
|
||||
}
|
||||
|
||||
fun changeLanguageVersion(module: Module, languageVersion: String?, apiVersion: String? = null, forTests: Boolean): PsiElement? {
|
||||
return changeBuildGradle(module) { buildScriptFile ->
|
||||
val manipulator = getManipulator(buildScriptFile)
|
||||
var result: PsiElement? = null
|
||||
if (languageVersion != null) {
|
||||
result = when (buildScriptFile) {
|
||||
is GroovyFile -> changeLanguageVersion(buildScriptFile, languageVersion, forTests)
|
||||
is KtFile -> changeLanguageVersion(buildScriptFile, languageVersion, forTests)
|
||||
else -> error("Unknown build script file type")
|
||||
}
|
||||
result = manipulator.changeLanguageVersion(languageVersion, forTests)
|
||||
}
|
||||
|
||||
if (apiVersion != null) {
|
||||
result = when (buildScriptFile) {
|
||||
is GroovyFile -> changeApiVersion(buildScriptFile, apiVersion, forTests)
|
||||
is KtFile -> changeApiVersion(buildScriptFile, apiVersion, forTests)
|
||||
else -> error("Unknown build script file type")
|
||||
}
|
||||
result = manipulator.changeApiVersion(apiVersion, forTests)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fun changeApiVersion(gradleFile: GroovyFile, apiVersion: String, forTests: Boolean): PsiElement? {
|
||||
return changeKotlinTaskParameter(gradleFile, "apiVersion", apiVersion, forTests)
|
||||
}
|
||||
|
||||
private fun changeKotlinTaskParameter(gradleFile: GroovyFile, parameterName: String, parameterValue: String, forTests: Boolean): PsiElement? {
|
||||
val snippet = "$parameterName = \"$parameterValue\""
|
||||
val kotlinBlock = getBlockOrCreate(gradleFile, if (forTests) "compileTestKotlin" else "compileKotlin")
|
||||
|
||||
for (stmt in kotlinBlock.statements) {
|
||||
if ((stmt as? GrAssignmentExpression)?.lValue?.text == "kotlinOptions." + parameterName) {
|
||||
return stmt.replaceWithStatementFromText("kotlinOptions." + snippet)
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinOptionsBlock = getBlockOrCreate(kotlinBlock, "kotlinOptions")
|
||||
addOrReplaceExpression(kotlinOptionsBlock, snippet) { stmt ->
|
||||
(stmt as? GrAssignmentExpression)?.lValue?.text == parameterName
|
||||
}
|
||||
return kotlinBlock.parent
|
||||
}
|
||||
|
||||
private fun changeBuildGradle(module: Module, body: (PsiFile) -> PsiElement?): PsiElement? {
|
||||
val buildScriptFile = module.getBuildScriptPsiFile()
|
||||
if (buildScriptFile != null && canConfigureFile(buildScriptFile)) {
|
||||
@@ -434,93 +240,15 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun addOrReplaceExpression(block: GrClosableBlock, snippet: String, predicate: (GrStatement) -> Boolean) {
|
||||
block.statements.firstOrNull(predicate)?.let { stmt ->
|
||||
stmt.replaceWithStatementFromText(snippet)
|
||||
return
|
||||
}
|
||||
addLastExpressionInBlockIfNeeded(snippet, block)
|
||||
}
|
||||
|
||||
private fun GrStatement.replaceWithStatementFromText(snippet: String): GrStatement {
|
||||
val newStatement = GroovyPsiElementFactory.getInstance(project).createExpressionFromText(snippet)
|
||||
CodeStyleManager.getInstance(project).reformat(newStatement)
|
||||
return replaceWithStatement(newStatement)
|
||||
}
|
||||
|
||||
fun getKotlinStdlibVersion(module: Module): String? {
|
||||
val gradleFile = module.getBuildScriptPsiFile() ?: return null
|
||||
return when (gradleFile) {
|
||||
is GroovyFile -> getKotlinStdlibVersion(gradleFile)
|
||||
is KtFile -> gradleFile.getKotlinStdlibVersion()
|
||||
else -> null
|
||||
return module.getBuildScriptPsiFile()?.let {
|
||||
getManipulator(it).getKotlinStdlibVersion()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getKotlinStdlibVersion(gradleFile: GroovyFile): String? {
|
||||
val versionProperty = "\$kotlin_version"
|
||||
val block = getBuildScriptBlock(gradleFile)
|
||||
if (block.text.contains("ext.kotlin_version = ")) {
|
||||
return versionProperty
|
||||
}
|
||||
fun configureProjectFile(file: PsiFile, version: String): Boolean = getManipulator(file).configureProjectBuildScript(version)
|
||||
|
||||
val dependencies = getDependenciesBlock(gradleFile).statements
|
||||
val stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:"
|
||||
for (dependency in dependencies) {
|
||||
val dependencyText = dependency.text
|
||||
val startIndex = dependencyText.indexOf(stdlibArtifactPrefix) + stdlibArtifactPrefix.length
|
||||
val endIndex = dependencyText.length - 1
|
||||
if (startIndex != -1 && endIndex != -1) {
|
||||
return dependencyText.substring(startIndex, endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
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)
|
||||
wasModified = addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version), buildScriptBlock)
|
||||
|
||||
val buildScriptRepositoriesBlock = getBuildScriptRepositoriesBlock(file)
|
||||
wasModified = wasModified or addRepository(buildScriptRepositoriesBlock, version)
|
||||
|
||||
val buildScriptDependenciesBlock = getBuildScriptDependenciesBlock(file)
|
||||
wasModified = wasModified or addLastExpressionInBlockIfNeeded(CLASSPATH, buildScriptDependenciesBlock)
|
||||
|
||||
return wasModified
|
||||
}
|
||||
|
||||
private fun isRepositoryConfigured(repositoriesBlockText: String): Boolean {
|
||||
return repositoriesBlockText.contains(MAVEN_CENTRAL) || repositoriesBlockText.contains(JCENTER)
|
||||
}
|
||||
|
||||
private fun canConfigureFile(file: PsiFile): Boolean {
|
||||
return WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
||||
}
|
||||
private fun canConfigureFile(file: PsiFile): Boolean = WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
|
||||
|
||||
private fun Module.getBuildScriptPsiFile() = getBuildScriptFile()?.getPsiFile(project)
|
||||
|
||||
@@ -555,74 +283,6 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
PsiManager.getInstance(project).findFile(it)
|
||||
}
|
||||
|
||||
private fun getDependenciesBlock(file: GrStatementOwner): GrClosableBlock {
|
||||
return getBlockOrCreate(file, "dependencies")
|
||||
}
|
||||
|
||||
private fun getBuildScriptBlock(file: GrStatementOwner) = getBlockOrCreate(file, "buildscript")
|
||||
|
||||
private fun getBuildScriptDependenciesBlock(file: GrStatementOwner): GrClosableBlock {
|
||||
val buildScript = getBuildScriptBlock(file)
|
||||
return getBlockOrCreate(buildScript, "dependencies")
|
||||
}
|
||||
|
||||
private fun getBuildScriptRepositoriesBlock(file: GrStatementOwner): GrClosableBlock {
|
||||
val buildScript = getBuildScriptBlock(file)
|
||||
return getBlockOrCreate(buildScript, "repositories")
|
||||
}
|
||||
|
||||
private fun getRepositoriesBlock(file: GrStatementOwner) = getBlockOrCreate(file, "repositories")
|
||||
|
||||
private fun getBlockOrCreate(parent: GrStatementOwner, name: String): GrClosableBlock {
|
||||
var block = getBlockByName(parent, name)
|
||||
if (block == null) {
|
||||
val factory = GroovyPsiElementFactory.getInstance(parent.project)
|
||||
val newBlock = factory.createExpressionFromText("$name{\n}\n")
|
||||
parent.addAfter(newBlock, parent.statements.lastOrNull() ?: parent.firstChild)
|
||||
block = getBlockByName(parent, name)!!
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
private fun addLastExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
|
||||
return addExpressionInBlockIfNeeded(text, block, false)
|
||||
}
|
||||
|
||||
private fun addFirstExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
|
||||
return addExpressionInBlockIfNeeded(text, block, true)
|
||||
}
|
||||
|
||||
private fun getBlockByName(parent: PsiElement, name: String): GrClosableBlock? {
|
||||
return parent.getChildrenOfType<GrMethodCallExpression>()
|
||||
.filter { it.closureArguments.isNotEmpty() }
|
||||
.find { it.invokedExpression.text == name }
|
||||
?.let { it.closureArguments[0] }
|
||||
}
|
||||
|
||||
private fun addExpressionInBlockIfNeeded(text: String, block: GrClosableBlock, isFirst: Boolean): Boolean {
|
||||
if (block.text.contains(text)) return false
|
||||
val newStatement = GroovyPsiElementFactory.getInstance(block.project).createExpressionFromText(text)
|
||||
CodeStyleManager.getInstance(block.project).reformat(newStatement)
|
||||
val statements = block.statements
|
||||
if (!isFirst && statements.isNotEmpty()) {
|
||||
val lastStatement = statements[statements.size - 1]
|
||||
if (lastStatement != null) {
|
||||
block.addAfter(newStatement, lastStatement)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val firstChild = block.firstChild
|
||||
if (firstChild != null) {
|
||||
block.addAfter(newStatement, firstChild)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getApplyStatement(file: GroovyFile): GrApplicationStatement? =
|
||||
file.getChildrenOfType<GrApplicationStatement>()
|
||||
.find { it.invokedExpression.text == "apply" }
|
||||
|
||||
private fun showErrorMessage(project: Project, message: String?) {
|
||||
Messages.showErrorDialog(project,
|
||||
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
|
||||
@@ -630,15 +290,5 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
|
||||
"Configure Kotlin-Gradle Plugin")
|
||||
}
|
||||
|
||||
private fun addRepository(repositoriesBlock: GrClosableBlock, version: String): Boolean {
|
||||
val repository = getRepositoryForVersion(version)
|
||||
val snippet = when {
|
||||
repository != null -> repository.toGroovyRepositorySnippet()
|
||||
!isRepositoryConfigured(repositoriesBlock.text) -> "$MAVEN_CENTRAL\n"
|
||||
else -> return false
|
||||
}
|
||||
return addLastExpressionInBlockIfNeeded(snippet, repositoriesBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* 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.gradle
|
||||
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinWithGradleConfigurator
|
||||
import org.jetbrains.kotlin.idea.configuration.changeCoroutineConfiguration
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
||||
|
||||
class KotlinWithGradleConfiguratorTest : LightCodeInsightFixtureTestCase() {
|
||||
fun testAddCoroutinesSupport() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle", "apply plugin: 'kotlin'\n") as GroovyFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(buildGradle, "enable")
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""apply plugin: 'kotlin'
|
||||
|kotlin {
|
||||
| experimental {
|
||||
| coroutines "enable"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddCoroutinesSupportGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts", "") as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
changeCoroutineConfiguration(buildGradle, "enable")
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.ENABLE
|
||||
|}""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testChangeCoroutinesSupport() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle",
|
||||
"""apply plugin: 'kotlin'
|
||||
|kotlin {
|
||||
| experimental {
|
||||
| coroutines "disable"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|")) as GroovyFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(buildGradle, "enable")
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""apply plugin: 'kotlin'
|
||||
|kotlin {
|
||||
| experimental {
|
||||
| coroutines "enable"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testChangeCoroutinesSupportGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.DISABLE
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
changeCoroutineConfiguration(buildGradle, "enable")
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.ENABLE
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddLanguageVersion() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle", "apply plugin: 'kotlin'\n") as GroovyFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(buildGradle, "1.1", false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""apply plugin: 'kotlin'
|
||||
|compileKotlin {
|
||||
| kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddLanguageVersionGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts", "") as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(buildGradle, "1.1", false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
|
||||
|val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
|}""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testChangeLanguageVersion() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle",
|
||||
"""apply plugin: 'kotlin'
|
||||
|compileKotlin {
|
||||
| kotlinOptions {
|
||||
| languageVersion = "1.0"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|")) as GroovyFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(buildGradle, "1.1", false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""apply plugin: 'kotlin'
|
||||
|compileKotlin {
|
||||
| kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
| }
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testChangeLanguageVersionGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.0"
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(buildGradle, "1.1", false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
|
||||
fun testAddLibrary() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle",
|
||||
"""
|
||||
|dependencies {
|
||||
| compile "org.jetbrains.kotlin:stdlib-jre8:1.0.0"
|
||||
|}
|
||||
|""".trimMargin("|")) as GroovyFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGroovyFile(
|
||||
buildGradle,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
|dependencies {
|
||||
| compile "org.jetbrains.kotlin:stdlib-jre8:1.0.0"
|
||||
| compile "org.jetbrains.kotlin:kotlin-reflect:1.0.0"
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddLibraryGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGSKFile(
|
||||
buildGradle,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| compile(kotlinModule("reflect", "1.0.0"))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddTestLibraryGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGSKFile(
|
||||
buildGradle,
|
||||
DependencyScope.TEST,
|
||||
object: ExternalLibraryDescriptor("junit", "junit", "4.12", "4.12") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGSKFile(
|
||||
buildGradle,
|
||||
DependencyScope.TEST,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-test", "1.1.2", "1.1.2") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
|dependencies {
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| testCompile("junit:junit:4.12")
|
||||
| testCompile(kotlinModule("test", "1.1.2"))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddLibraryGSK_WithKotlinVersion() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""
|
||||
|val kotlin_version: String by extra
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8", kotlin_version))
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
val stdLibVersion = KotlinWithGradleConfigurator.getKotlinStdlibVersion(myFixture.module)
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGSKFile(
|
||||
buildGradle,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", stdLibVersion, stdLibVersion) {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
|val kotlin_version: String by extra
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8", kotlin_version))
|
||||
| compile(kotlinModule("reflect", kotlin_version))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
|
||||
fun testAddNonKotlinLibraryGSK() {
|
||||
val buildGradle = myFixture.configureByText("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|")) as KtFile
|
||||
myFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModuleGSKFile(
|
||||
buildGradle,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.a.b", "lib", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
},
|
||||
false)
|
||||
}
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| compile("org.a.b:lib:1.0.0")
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,10 @@ import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ExternalLibraryDescriptor
|
||||
import org.jetbrains.kotlin.idea.configuration.*
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.test.testFramework.runInEdtAndWait
|
||||
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||
@@ -208,4 +211,545 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
||||
assertSameElements(moduleNamesWithKotlinFiles, "app")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddNonKotlinLibraryGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.a.b", "lib", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| compile("org.a.b:lib:1.0.0")
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddLibraryGSK_WithKotlinVersion() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""
|
||||
|val kotlin_version: String by extra
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8", kotlin_version))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
val stdLibVersion = KotlinWithGradleConfigurator.getKotlinStdlibVersion(myTestFixture.module)
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.COMPILE,
|
||||
object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", stdLibVersion, stdLibVersion) {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|val kotlin_version: String by extra
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8", kotlin_version))
|
||||
| compile(kotlinModule("reflect", kotlin_version))
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddTestLibraryGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.TEST,
|
||||
object : ExternalLibraryDescriptor("junit", "junit", "4.12", "4.12") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.TEST,
|
||||
object : ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-test", "1.1.2", "1.1.2") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|dependencies {
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| testCompile("junit:junit:4.12")
|
||||
| testCompile(kotlinModule("test", "1.1.2"))
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddLibraryGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""
|
||||
|dependencies {
|
||||
| testCompile("junit:junit:4.12")
|
||||
| compile(kotlinModule("stdlib-jre8"))
|
||||
| compile(kotlinModule("reflect", "1.0.0"))
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddCoroutinesSupport() {
|
||||
val buildScript = createProjectSubFile("build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(myTestFixture.module, "enable")
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals("""
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddCoroutinesSupportGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts", "")
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(myTestFixture.module, "enable")
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.ENABLE
|
||||
|}""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeCoroutinesSupport() {
|
||||
val buildScript = createProjectSubFile("build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "error"
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(myTestFixture.module, "enable")
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals("""
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeCoroutinesSupportGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.DISABLE
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeCoroutineConfiguration(myTestFixture.module, "enable")
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||
|
|
||||
|kotlin {
|
||||
| experimental.coroutines = Coroutines.ENABLE
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddLanguageVersion() {
|
||||
val buildScript = createProjectSubFile("build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(myTestFixture.module, "1.1", null, false)
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals("""
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
languageVersion = "1.1"
|
||||
}
|
||||
}
|
||||
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddLanguageVersionGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts", "")
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(myTestFixture.module, "1.1", null, false)
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
|
||||
|val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
|}""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeLanguageVersion() {
|
||||
val buildScript = createProjectSubFile("build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
languageVersion = "1.0"
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(myTestFixture.module, "1.1", null, false)
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals("""
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
languageVersion = "1.1"
|
||||
}
|
||||
}
|
||||
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChangeLanguageVersionGSK() {
|
||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||
"""val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.0"
|
||||
|}
|
||||
|""".trimMargin("|"))
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.changeLanguageVersion(myTestFixture.module, "1.1", null, false)
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
"""val compileKotlin: KotlinCompile by tasks
|
||||
|compileKotlin.kotlinOptions {
|
||||
| languageVersion = "1.1"
|
||||
|}
|
||||
|""".trimMargin("|"),
|
||||
LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddLibrary() {
|
||||
val buildScript = createProjectSubFile("build.gradle", """
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
importProject()
|
||||
|
||||
runInEdtAndWait {
|
||||
myTestFixture.project.executeWriteCommand("") {
|
||||
KotlinWithGradleConfigurator.addKotlinLibraryToModule(
|
||||
myTestFixture.module,
|
||||
DependencyScope.COMPILE,
|
||||
object: ExternalLibraryDescriptor("org.jetbrains.kotlin", "kotlin-reflect", "1.0.0", "1.0.0") {
|
||||
override fun getLibraryClassesRoots() = emptyList<String>()
|
||||
})
|
||||
}
|
||||
|
||||
FileDocumentManager.getInstance().saveAllDocuments()
|
||||
}
|
||||
|
||||
assertEquals("""
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-1.1.0"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:1.0.0"
|
||||
}
|
||||
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ abstract class AbstractGradleConfigureProjectByChangingFileTest : AbstractConfig
|
||||
return
|
||||
}
|
||||
|
||||
configurator.changeBuildScript(file, true, version, collector)
|
||||
configurator.changeBuildScript(file, false, version, collector)
|
||||
configurator.configureBuildScript(file, true, version, collector)
|
||||
configurator.configureBuildScript(file, false, version, collector)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user