User: joe Date: 01 Mar 23 14:27 Revision: 522df0066eaa7dd6bd840d0364d70570b255d297 Summary: Make kotlin dependency optional TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=8350&personal=false Index: src/main/kotlin/creator/buildsystem/GradleFile.kt =================================================================== --- src/main/kotlin/creator/buildsystem/GradleFile.kt (revision 522df0066eaa7dd6bd840d0364d70570b255d297) +++ src/main/kotlin/creator/buildsystem/GradleFile.kt (revision 522df0066eaa7dd6bd840d0364d70570b255d297) @@ -0,0 +1,232 @@ +/* + * Minecraft Dev for IntelliJ + * + * https://minecraftdev.org + * + * Copyright (c) 2023 minecraft-dev + * + * MIT License + */ + +package com.demonwav.mcdev.creator.buildsystem + +import com.demonwav.mcdev.util.childrenOfType +import com.demonwav.mcdev.util.mapFirstNotNull +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus.Internal +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtScriptInitializer +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.blocks.GrClosableBlock +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression +import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner + +@Internal +interface GradleFile { + companion object { + val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.gradleFileType") + } + + val psi: PsiFile + + fun addRepositories(project: Project, repositories: List) + fun addDependencies(project: Project, dependencies: List) + fun addPlugins(project: Project, plugins: List) + + interface Type { + fun createGradleFile(psiFile: PsiFile): GradleFile? + } +} + +class GroovyGradleFile(override val psi: GroovyFile) : GradleFile { + override fun addRepositories(project: Project, repositories: List) { + val reposBlock = findOrCreateGroovyBlock(project, psi, "repositories") + val elementFactory = GroovyPsiElementFactory.getInstance(project) + for (repo in repositories) { + if (BuildSystemType.GRADLE !in repo.buildSystems) { + continue + } + val mavenBlock = + elementFactory.createStatementFromText("maven {\n}", reposBlock) as GrMethodCallExpression + val mavenClosure = mavenBlock.closureArguments[0] + if (repo.id.isNotBlank()) { + val idStatement = + elementFactory.createStatementFromText("name = ${makeStringLiteral(repo.id)}") + mavenClosure.addStatementBefore(idStatement, null) + } + val urlStatement = + elementFactory.createStatementFromText("url = ${makeStringLiteral(repo.url)}") + mavenClosure.addStatementBefore(urlStatement, null) + reposBlock.addStatementBefore(mavenBlock, null) + } + } + + override fun addDependencies(project: Project, dependencies: List) { + val depsBlock = findOrCreateGroovyBlock(project, psi, "dependencies") + val elementFactory = GroovyPsiElementFactory.getInstance(project) + for (dep in dependencies) { + val gradleConfig = dep.gradleConfiguration ?: continue + val stmt = elementFactory.createStatementFromText( + "$gradleConfig \"${escapeGString(dep.groupId)}:${ + escapeGString(dep.artifactId) + }:${escapeGString(dep.version)}\"", + depsBlock, + ) + depsBlock.addStatementBefore(stmt, null) + } + } + + override fun addPlugins(project: Project, plugins: List) { + val pluginsBlock = findOrCreateGroovyBlock(project, psi, "plugins", first = true) + val elementFactory = GroovyPsiElementFactory.getInstance(project) + for (plugin in plugins) { + val stmt = elementFactory.createStatementFromText(makePluginStatement(plugin, false)) + pluginsBlock.addStatementBefore(stmt, null) + } + } + + private fun findGroovyBlock(element: GrStatementOwner, name: String): GrClosableBlock? { + return element.statements + .mapFirstNotNull { call -> + if (call is GrMethodCallExpression && call.callReference?.methodName == name) { + call.closureArguments.firstOrNull() + } else { + null + } + } + } + + private fun findOrCreateGroovyBlock( + project: Project, + element: GrStatementOwner, + name: String, + first: Boolean = false, + ): GrClosableBlock { + findGroovyBlock(element, name)?.let { return it } + val block = GroovyPsiElementFactory.getInstance(project).createStatementFromText("$name {\n}", element) + val anchor = if (first) { + element.statements.firstOrNull() + } else { + null + } + return (element.addStatementBefore(block, anchor) as GrMethodCallExpression).closureArguments.first() + } + + class Type : GradleFile.Type { + override fun createGradleFile(psiFile: PsiFile) = (psiFile as? GroovyFile)?.let(::GroovyGradleFile) + } +} + +class KotlinGradleFile(override val psi: KtFile) : GradleFile { + override fun addRepositories(project: Project, repositories: List) { + val script = psi.script?.blockExpression ?: return + val reposBlock = findOrCreateKotlinBlock(project, script, "repositories") + val elementFactory = KtPsiFactory(project) + for (repo in repositories) { + if (BuildSystemType.GRADLE !in repo.buildSystems) { + continue + } + val mavenBlock = elementFactory.createExpression("maven {\n}") as KtCallExpression + val mavenLambda = mavenBlock.lambdaArguments[0].getLambdaExpression()!!.bodyExpression!! + if (repo.id.isNotBlank()) { + val idStatement = elementFactory.createAssignment("name = ${makeStringLiteral(repo.id)}") + mavenLambda.addBefore(idStatement, mavenLambda.rBrace) + } + val urlStatement = elementFactory.createAssignment("url = uri(${makeStringLiteral(repo.url)})") + mavenLambda.addBefore(urlStatement, mavenLambda.rBrace) + reposBlock.addBefore(mavenBlock, reposBlock.rBrace) + } + } + + override fun addDependencies(project: Project, dependencies: List) { + val script = psi.script?.blockExpression ?: return + val depsBlock = findOrCreateKotlinBlock(project, script, "dependencies") + val elementFactory = KtPsiFactory(project) + for (dep in dependencies) { + val gradleConfig = dep.gradleConfiguration ?: continue + val stmt = elementFactory.createExpression( + "$gradleConfig(\"${escapeGString(dep.groupId)}:${ + escapeGString(dep.artifactId) + }:${escapeGString(dep.version)}\")", + ) + depsBlock.addBefore(stmt, depsBlock.rBrace) + } + } + + override fun addPlugins(project: Project, plugins: List) { + val script = psi.script?.blockExpression ?: return + val pluginsBlock = findOrCreateKotlinBlock(project, script, "plugins", first = true) + val elementFactory = KtPsiFactory(project) + for (plugin in plugins) { + val stmt = elementFactory.createExpression(makePluginStatement(plugin, true)) + pluginsBlock.addBefore(stmt, pluginsBlock.rBrace) + } + } + + private fun findKotlinBlock(element: KtBlockExpression, name: String): KtBlockExpression? { + return element.childrenOfType() + .flatMap { it.childrenOfType() } + .mapFirstNotNull { call -> + if ((call.calleeExpression as? KtNameReferenceExpression)?.getReferencedName() == name) { + call.lambdaArguments.firstOrNull()?.getLambdaExpression()?.bodyExpression + } else { + null + } + } + } + + private fun findOrCreateKotlinBlock( + project: Project, + element: KtBlockExpression, + name: String, + first: Boolean = false, + ): KtBlockExpression { + findKotlinBlock(element, name)?.let { return it } + val block = KtPsiFactory(project).createExpression("$name {\n}") + val addedBlock = if (first) { + element.addAfter(block, element.lBrace) + } else { + element.addBefore(block, element.rBrace) + } + return (addedBlock as KtCallExpression).lambdaArguments.first().getLambdaExpression()!!.bodyExpression!! + } + + private fun KtPsiFactory.createAssignment(text: String): KtBinaryExpression { + return this.createBlock(text).firstStatement as KtBinaryExpression + } + + class Type : GradleFile.Type { + override fun createGradleFile(psiFile: PsiFile) = (psiFile as? KtFile)?.let(::KotlinGradleFile) + } +} + +private fun makeStringLiteral(str: String): String { + return "\"${escapeGString(str)}\"" +} + +private fun escapeGString(str: String): String { + return StringUtil.escapeStringCharacters(str.length, str, "\"\$", StringBuilder()).toString() +} + +private fun makePluginStatement(plugin: GradlePlugin, kotlin: Boolean): String { + return buildString { + if (kotlin) { + append("id(${makeStringLiteral(plugin.id)})") + } else { + append("id ${makeStringLiteral(plugin.id)}") + } + plugin.version?.let { append(" version ${makeStringLiteral(it)}") } + if (!plugin.apply) { + append(" apply false") + } + } +} Index: src/main/kotlin/creator/buildsystem/gradle-steps.kt =================================================================== --- src/main/kotlin/creator/buildsystem/gradle-steps.kt (revision 1a8b7499f2141f08f596d1381a2ca304641d7848) +++ src/main/kotlin/creator/buildsystem/gradle-steps.kt (revision 522df0066eaa7dd6bd840d0364d70570b255d297) @@ -17,7 +17,6 @@ import com.demonwav.mcdev.creator.step.FixedAssetsNewProjectWizardStep import com.demonwav.mcdev.util.MinecraftTemplates.Companion.GRADLE_WRAPPER_PROPERTIES import com.demonwav.mcdev.util.SemanticVersion -import com.demonwav.mcdev.util.childrenOfType import com.demonwav.mcdev.util.invokeAndWait import com.demonwav.mcdev.util.invokeLater import com.demonwav.mcdev.util.mapFirstNotNull @@ -35,7 +34,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.startup.StartupManager import com.intellij.openapi.util.Key -import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.wm.WindowManager @@ -45,22 +43,10 @@ import com.intellij.psi.PsiManager import java.nio.file.Path import java.util.concurrent.CountDownLatch -import org.jetbrains.kotlin.psi.KtBinaryExpression -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtNameReferenceExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtScriptInitializer import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration import org.jetbrains.plugins.gradle.service.project.open.canLinkAndRefreshGradleProject import org.jetbrains.plugins.gradle.service.project.open.linkAndRefreshGradleProject -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.blocks.GrClosableBlock -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression -import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner val DEFAULT_GRADLE_VERSION = SemanticVersion.release(7, 3, 3) val GRADLE_VERSION_KEY = Key.create("mcdev.gradleVersion") @@ -98,201 +84,30 @@ } buildGradle.psi.runWriteAction { - when (buildGradle) { - is GroovyGradleFile -> { - val reposBlock = findOrCreateGroovyBlock(project, buildGradle.psi, "repositories") - val elementFactory = GroovyPsiElementFactory.getInstance(project) - for (repo in repositories) { - if (BuildSystemType.GRADLE !in repo.buildSystems) { - continue + buildGradle.addRepositories(project, repositories) - } + } - val mavenBlock = - elementFactory.createStatementFromText("maven {\n}", reposBlock) as GrMethodCallExpression - val mavenClosure = mavenBlock.closureArguments[0] - if (repo.id.isNotBlank()) { - val idStatement = - elementFactory.createStatementFromText("name = ${makeStringLiteral(repo.id)}") - mavenClosure.addStatementBefore(idStatement, null) - } + } - val urlStatement = - elementFactory.createStatementFromText("url = ${makeStringLiteral(repo.url)}") - mavenClosure.addStatementBefore(urlStatement, null) - reposBlock.addStatementBefore(mavenBlock, null) - } - } - is KotlinGradleFile -> { - val script = buildGradle.psi.script?.blockExpression ?: return@runWriteAction - val reposBlock = findOrCreateKotlinBlock(project, script, "repositories") - val elementFactory = KtPsiFactory(project) - for (repo in repositories) { - if (BuildSystemType.GRADLE !in repo.buildSystems) { - continue - } - val mavenBlock = elementFactory.createExpression("maven {\n}") as KtCallExpression - val mavenLambda = mavenBlock.lambdaArguments[0].getLambdaExpression()!!.bodyExpression!! - if (repo.id.isNotBlank()) { - val idStatement = elementFactory.createAssignment("name = ${makeStringLiteral(repo.id)}") - mavenLambda.addBefore(idStatement, mavenLambda.rBrace) - } - val urlStatement = elementFactory.createAssignment("url = uri(${makeStringLiteral(repo.url)})") - mavenLambda.addBefore(urlStatement, mavenLambda.rBrace) - reposBlock.addBefore(mavenBlock, reposBlock.rBrace) - } - } - } - } - } - protected fun addDependencies(project: Project, buildGradle: GradleFile?, dependencies: List) { if (buildGradle == null || dependencies.isEmpty()) { return } buildGradle.psi.runWriteAction { - when (buildGradle) { - is GroovyGradleFile -> { - val depsBlock = findOrCreateGroovyBlock(project, buildGradle.psi, "dependencies") - val elementFactory = GroovyPsiElementFactory.getInstance(project) - for (dep in dependencies) { - val gradleConfig = dep.gradleConfiguration ?: continue - val stmt = elementFactory.createStatementFromText( - "$gradleConfig \"${escapeGString(dep.groupId)}:${ - escapeGString(dep.artifactId) - }:${escapeGString(dep.version)}\"", - depsBlock, - ) - depsBlock.addStatementBefore(stmt, null) + buildGradle.addDependencies(project, dependencies) - } - } + } + } - is KotlinGradleFile -> { - val script = buildGradle.psi.script?.blockExpression ?: return@runWriteAction - val depsBlock = findOrCreateKotlinBlock(project, script, "dependencies") - val elementFactory = KtPsiFactory(project) - for (dep in dependencies) { - val gradleConfig = dep.gradleConfiguration ?: continue - val stmt = elementFactory.createExpression( - "$gradleConfig(\"${escapeGString(dep.groupId)}:${ - escapeGString(dep.artifactId) - }:${escapeGString(dep.version)}\")", - ) - depsBlock.addBefore(stmt, depsBlock.rBrace) - } - } - } - } - } - protected fun addPlugins(project: Project, buildGradle: GradleFile?, plugins: List) { if (buildGradle == null || plugins.isEmpty()) { return } buildGradle.psi.runWriteAction { - fun makePluginStatement(plugin: GradlePlugin, kotlin: Boolean): String { - return buildString { - if (kotlin) { - append("id(${makeStringLiteral(plugin.id)})") - } else { - append("id ${makeStringLiteral(plugin.id)}") + buildGradle.addPlugins(project, plugins) - } + } - plugin.version?.let { append(" version ${makeStringLiteral(it)}") } - if (!plugin.apply) { - append(" apply false") - } + } - } - } - when (buildGradle) { - is GroovyGradleFile -> { - val pluginsBlock = findOrCreateGroovyBlock(project, buildGradle.psi, "plugins", first = true) - val elementFactory = GroovyPsiElementFactory.getInstance(project) - for (plugin in plugins) { - val stmt = elementFactory.createStatementFromText(makePluginStatement(plugin, false)) - pluginsBlock.addStatementBefore(stmt, null) - } - } - is KotlinGradleFile -> { - val script = buildGradle.psi.script?.blockExpression ?: return@runWriteAction - val pluginsBlock = findOrCreateKotlinBlock(project, script, "plugins", first = true) - val elementFactory = KtPsiFactory(project) - for (plugin in plugins) { - val stmt = elementFactory.createExpression(makePluginStatement(plugin, true)) - pluginsBlock.addBefore(stmt, pluginsBlock.rBrace) - } - } - } - } - } - - protected fun makeStringLiteral(str: String): String { - return "\"${escapeGString(str)}\"" - } - - private fun escapeGString(str: String): String { - return StringUtil.escapeStringCharacters(str.length, str, "\"\$", StringBuilder()).toString() - } - - protected fun findGroovyBlock(element: GrStatementOwner, name: String): GrClosableBlock? { - return element.statements - .mapFirstNotNull { call -> - if (call is GrMethodCallExpression && call.callReference?.methodName == name) { - call.closureArguments.firstOrNull() - } else { - null - } - } - } - - protected fun findOrCreateGroovyBlock( - project: Project, - element: GrStatementOwner, - name: String, - first: Boolean = false, - ): GrClosableBlock { - findGroovyBlock(element, name)?.let { return it } - val block = GroovyPsiElementFactory.getInstance(project).createStatementFromText("$name {\n}", element) - val anchor = if (first) { - element.statements.firstOrNull() - } else { - null - } - return (element.addStatementBefore(block, anchor) as GrMethodCallExpression).closureArguments.first() - } - - protected fun findKotlinBlock(element: KtBlockExpression, name: String): KtBlockExpression? { - return element.childrenOfType() - .flatMap { it.childrenOfType() } - .mapFirstNotNull { call -> - if ((call.calleeExpression as? KtNameReferenceExpression)?.getReferencedName() == name) { - call.lambdaArguments.firstOrNull()?.getLambdaExpression()?.bodyExpression - } else { - null - } - } - } - - protected fun findOrCreateKotlinBlock( - project: Project, - element: KtBlockExpression, - name: String, - first: Boolean = false, - ): KtBlockExpression { - findKotlinBlock(element, name)?.let { return it } - val block = KtPsiFactory(project).createExpression("$name {\n}") - val addedBlock = if (first) { - element.addAfter(block, element.lBrace) - } else { - element.addBefore(block, element.rBrace) - } - return (addedBlock as KtCallExpression).lambdaArguments.first().getLambdaExpression()!!.bodyExpression!! - } - - protected fun KtPsiFactory.createAssignment(text: String): KtBinaryExpression { - return this.createBlock(text).firstStatement as KtBinaryExpression - } - override fun perform(project: Project) { invokeAndWait { if (project.isDisposed) { @@ -336,12 +151,8 @@ private fun makeGradleFile(virtualFile: VirtualFile): GradleFile? { val psi = PsiManager.getInstance(project).findFile(virtualFile) ?: return null - return when (psi) { - is GroovyFile -> GroovyGradleFile(psi) - is KtFile -> KotlinGradleFile(psi) - else -> null + return GradleFile.EP_NAME.extensions.mapFirstNotNull { it.createGradleFile(psi) } - } + } - } fun commit() { val files = mutableListOf() @@ -363,13 +174,7 @@ } } } - - sealed class GradleFile { - abstract val psi: PsiFile - } +} - class GroovyGradleFile(override val psi: GroovyFile) : GradleFile() - class KotlinGradleFile(override val psi: KtFile) : GradleFile() -} open class GradleImportStep(parent: NewProjectWizardStep) : AbstractLongRunningStep(parent) { override val description = "Importing Gradle project" Index: src/main/resources/META-INF/mcdev-kotlin.xml =================================================================== --- src/main/resources/META-INF/mcdev-kotlin.xml (revision 522df0066eaa7dd6bd840d0364d70570b255d297) +++ src/main/resources/META-INF/mcdev-kotlin.xml (revision 522df0066eaa7dd6bd840d0364d70570b255d297) @@ -0,0 +1,15 @@ + + + + + + + Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 1a8b7499f2141f08f596d1381a2ca304641d7848) +++ src/main/resources/META-INF/plugin.xml (revision 522df0066eaa7dd6bd840d0364d70570b255d297) @@ -13,7 +13,7 @@ com.intellij.modules.java org.jetbrains.idea.maven com.intellij.gradle - org.jetbrains.kotlin + org.jetbrains.kotlin org.intellij.groovy com.intellij.properties ByteCodeViewer @@ -47,6 +47,8 @@ + + @@ -92,6 +94,8 @@ + +