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 = ")) {
|
||||
|
||||
Reference in New Issue
Block a user