Implement Add Kotlin library quickfix for GSK
This commit is contained in:
@@ -24,11 +24,14 @@ import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
|
||||
|
||||
fun getKotlinScriptDependencySnippet(artifactName: String): String =
|
||||
"compile(${getKotlinModuleDependencySnippet(artifactName)})"
|
||||
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(artifactName: String): String =
|
||||
"kotlinModule(\"${artifactName.removePrefix("kotlin-")}\", $GSK_KOTLIN_VERSION_PROPERTY_NAME)"
|
||||
fun getKotlinModuleDependencySnippet(artifactId: String, version: String? = null): String =
|
||||
"kotlinModule(\"${artifactId.removePrefix("kotlin-")}\", ${version?.let { "\"$it\"" } ?: GSK_KOTLIN_VERSION_PROPERTY_NAME})"
|
||||
|
||||
fun KtFile.containsCompileStdLib(): Boolean =
|
||||
findScriptInitializer("dependencies")?.getBlock()?.findCompileStdLib() != null
|
||||
@@ -36,12 +39,30 @@ fun KtFile.containsCompileStdLib(): Boolean =
|
||||
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")
|
||||
|
||||
@@ -58,13 +79,6 @@ fun KtFile.createApplyBlock(): KtBlockExpression? {
|
||||
|
||||
fun KtFile.getApplyBlock(): KtBlockExpression? = findScriptInitializer("apply")?.getBlock() ?: createApplyBlock()
|
||||
|
||||
private fun KtExpression.isKotlinStdLib(): Boolean = when (this) {
|
||||
is KtCallExpression -> calleeExpression?.text == "kotlinModule" &&
|
||||
valueArguments.firstOrNull()?.getArgumentExpression()?.text == "\"stdlib\""
|
||||
is KtStringTemplateExpression -> text.startsWith("\"org.jetbrains.kotlin:kotlin-stdlib:")
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun KtBlockExpression.findPlugin(pluginName: String): KtCallExpression? {
|
||||
return PsiTreeUtil.getChildrenOfType(this, KtCallExpression::class.java)?.find {
|
||||
it.calleeExpression?.text == "plugin" && it.valueArguments.firstOrNull()?.text == "\"$pluginName\""
|
||||
@@ -215,7 +229,6 @@ fun KtBlockExpression.addDeclarationIfMissing(text: String, first: Boolean = fal
|
||||
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
|
||||
}
|
||||
@@ -232,5 +245,7 @@ private val PsiElement.psiFactory: KtPsiFactory
|
||||
|
||||
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"
|
||||
@@ -175,7 +175,7 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
|
||||
private fun KtBlockExpression.addCompileStdlibIfMissing(sdk: Sdk?, version: String): KtCallExpression? =
|
||||
findCompileStdLib() ?:
|
||||
addExpressionIfMissing(getKotlinScriptDependencySnippet(getStdlibArtifactName(sdk, version))) as? KtCallExpression
|
||||
addExpressionIfMissing(getGSKCompileDependencySnippet(KOTLIN_GROUP_ID, getStdlibArtifactName(sdk, version))) as? KtCallExpression
|
||||
|
||||
protected fun addElementsToModuleGroovyFile(file: GroovyFile, version: String): Boolean {
|
||||
val oldText = file.text
|
||||
@@ -281,41 +281,71 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
fun getGroovyApplyPluginDirective(pluginName: String) = "apply plugin: '$pluginName'"
|
||||
|
||||
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
|
||||
val gradleFile = module.getBuildScriptPsiFile() as? GroovyFile
|
||||
if (gradleFile != null && canConfigureFile(gradleFile)) {
|
||||
gradleFile.project.executeWriteCommand("Add Kotlin library") {
|
||||
val groovyScope = when (scope) {
|
||||
DependencyScope.COMPILE -> "compile"
|
||||
DependencyScope.TEST -> if (KotlinPluginUtil.isAndroidGradleModule(module)) {
|
||||
// TODO we should add testCompile or androidTestCompile
|
||||
"compile"
|
||||
}
|
||||
else {
|
||||
"testCompile"
|
||||
}
|
||||
DependencyScope.RUNTIME -> "runtime"
|
||||
DependencyScope.PROVIDED -> "compile"
|
||||
else -> "compile"
|
||||
}
|
||||
|
||||
val dependencyString = String.format(
|
||||
"%s \"%s:%s:%s\"",
|
||||
groovyScope, libraryDescriptor.libraryGroupId, libraryDescriptor.libraryArtifactId,
|
||||
libraryDescriptor.maxVersion)
|
||||
|
||||
val dependenciesBlock = getDependenciesBlock(gradleFile)
|
||||
addLastExpressionInBlockIfNeeded(dependencyString, dependenciesBlock)
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(gradleFile)
|
||||
}
|
||||
|
||||
val virtualFile = gradleFile.virtualFile
|
||||
if (virtualFile != null) {
|
||||
createConfigureKotlinNotificationCollector(gradleFile.project)
|
||||
.addMessage(virtualFile.path + " was modified")
|
||||
.showNotification()
|
||||
}
|
||||
val buildScript = module.getBuildScriptPsiFile() ?: return
|
||||
if (!canConfigureFile(buildScript)) {
|
||||
return
|
||||
}
|
||||
|
||||
val isAndroidModule = KotlinPluginUtil.isAndroidGradleModule(module)
|
||||
when (buildScript) {
|
||||
is GroovyFile -> addKotlinLibraryToModuleGroovyFile(buildScript, scope, libraryDescriptor, isAndroidModule)
|
||||
is KtFile -> addKotlinLibraryToModuleGSKFile(buildScript, scope, libraryDescriptor, isAndroidModule)
|
||||
}
|
||||
|
||||
buildScript.virtualFile?.let {
|
||||
createConfigureKotlinNotificationCollector(buildScript.project)
|
||||
.addMessage(it.path + " was modified")
|
||||
.showNotification()
|
||||
}
|
||||
}
|
||||
|
||||
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? {
|
||||
@@ -419,7 +449,15 @@ abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||
}
|
||||
|
||||
fun getKotlinStdlibVersion(module: Module): String? {
|
||||
val gradleFile = module.getBuildScriptPsiFile() as? GroovyFile ?: return null
|
||||
val gradleFile = module.getBuildScriptPsiFile() ?: return null
|
||||
return when (gradleFile) {
|
||||
is GroovyFile -> getKotlinStdlibVersion(gradleFile)
|
||||
is KtFile -> gradleFile.getKotlinStdlibVersion()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getKotlinStdlibVersion(gradleFile: GroovyFile): String? {
|
||||
val versionProperty = "\$kotlin_version"
|
||||
val block = getBuildScriptBlock(gradleFile)
|
||||
if (block.text.contains("ext.kotlin_version = ")) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
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
|
||||
@@ -162,4 +164,148 @@ class KotlinWithGradleConfiguratorTest : LightCodeInsightFixtureTestCase() {
|
||||
|}
|
||||
|""".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("|"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user