From 10d4ca6e7792fdac60e2f8dda1f535e873397ecf Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 30 Apr 2024 19:49:53 +0200 Subject: [PATCH 001/118] Initial custom template system --- .../EvaluateTemplateExpressionAction.kt | 61 ++++ .../creator/custom/TemplateDescriptor.kt | 20 ++ .../creator/custom/TemplateEvaluator.kt | 25 ++ .../kotlin/creator/custom/model/ClassFqn.kt | 11 + .../platformtype/CustomPlatformStep.kt | 261 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 4 + .../messages/MinecraftDevelopment.properties | 11 + 7 files changed, 393 insertions(+) create mode 100644 src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt create mode 100644 src/main/kotlin/creator/custom/TemplateDescriptor.kt create mode 100644 src/main/kotlin/creator/custom/TemplateEvaluator.kt create mode 100644 src/main/kotlin/creator/custom/model/ClassFqn.kt create mode 100644 src/main/kotlin/creator/platformtype/CustomPlatformStep.kt diff --git a/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt b/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt new file mode 100644 index 000000000..4ea8b0a92 --- /dev/null +++ b/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt @@ -0,0 +1,61 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.util.Disposer +import com.intellij.ui.components.JBTextField +import com.intellij.ui.dsl.builder.Align +import com.intellij.ui.dsl.builder.panel +import javax.swing.JComponent + +class EvaluateTemplateExpressionAction : AnAction() { + override fun actionPerformed(e: AnActionEvent) { + + val dialog = EvaluateDialog() + dialog.isModal = false + dialog.show() + } + + private class EvaluateDialog : DialogWrapper(null, false, IdeModalityType.IDE) { + val document = EditorFactory.getInstance().createDocument("") + val editor = EditorFactory.getInstance().createEditor(document) as EditorEx + + lateinit var field: JBTextField + + init { + title = "Evaluate Template Expression" + isOKActionEnabled = true + setValidationDelay(0) + + Disposer.register(disposable) { + EditorFactory.getInstance().releaseEditor(editor) + } + + init() + } + + override fun createCenterPanel(): JComponent = panel { + row { + cell(editor.component).align(Align.FILL) + } + + row("Result:") { + field = textField().align(Align.FILL).component + field.isEditable = false + } + } + + override fun doOKAction() { + val props = mapOf( + "BUILD_SYSTEM" to "gradle", + "USE_PAPER_MANIFEST" to false, + "MAIN_CLASS" to ClassFqn("io.github.rednesto.test.Test") + ) + field.text = TemplateEvaluator.evaluate(props, document.text).toString() + } + } +} diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt new file mode 100644 index 000000000..83986d0ec --- /dev/null +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -0,0 +1,20 @@ +package com.demonwav.mcdev.creator.custom + +data class TemplateDescriptor( + val properties: List, + val files: List +) + +data class TemplateProperty( + val name: String, + val type: String, + val label: String, + val options: List, + val default: Any +) + +data class TemplateFile( + val template: String, + val destination: String, + val condition: String? = null +) diff --git a/src/main/kotlin/creator/custom/TemplateEvaluator.kt b/src/main/kotlin/creator/custom/TemplateEvaluator.kt new file mode 100644 index 000000000..69cdcca8d --- /dev/null +++ b/src/main/kotlin/creator/custom/TemplateEvaluator.kt @@ -0,0 +1,25 @@ +package com.demonwav.mcdev.creator.custom + +import org.apache.velocity.VelocityContext +import org.apache.velocity.app.Velocity +import org.apache.velocity.util.StringBuilderWriter + +object TemplateEvaluator { + + fun evaluate(properties: Map, template: String): Result> { + val context = VelocityContext(properties) + val stringWriter = StringBuilderWriter() + return runCatching { + Velocity.evaluate(context, stringWriter, "McDevTplExpr", template) to stringWriter.toString() + } + } + + fun template(properties: Map, template: String): Result { + return evaluate(properties, template).map { it.second } + } + + fun condition(properties: Map, condition: String): Result { + val actualCondition = "#if ($condition) true#else false#end" + return evaluate(properties, actualCondition).map { it.second.toBoolean() } + } +} diff --git a/src/main/kotlin/creator/custom/model/ClassFqn.kt b/src/main/kotlin/creator/custom/model/ClassFqn.kt new file mode 100644 index 000000000..8641b954d --- /dev/null +++ b/src/main/kotlin/creator/custom/model/ClassFqn.kt @@ -0,0 +1,11 @@ +package com.demonwav.mcdev.creator.custom.model + +data class ClassFqn(val fqn: String) { + + val className by lazy { fqn.substringAfterLast('.') } + val path by lazy { fqn.replace('.', '/') } + val packageName by lazy { fqn.substringBeforeLast('.') } + val packagePath by lazy { packageName.replace('.', '/') } + + override fun toString(): String = fqn +} diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt new file mode 100644 index 000000000..70954ff85 --- /dev/null +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -0,0 +1,261 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.platformtype + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer +import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep +import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.demonwav.mcdev.creator.findStep +import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep +import com.demonwav.mcdev.util.fromJson +import com.google.gson.Gson +import com.intellij.ide.fileTemplates.impl.CustomFileTemplate +import com.intellij.ide.starters.local.GeneratorTemplateFile +import com.intellij.ide.wizard.NewProjectWizardBaseData +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.ObservableMutableProperty +import com.intellij.openapi.observable.util.bindBooleanStorage +import com.intellij.openapi.observable.util.bindStorage +import com.intellij.openapi.observable.util.toStringProperty +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.Task +import com.intellij.openapi.progress.withBackgroundProgress +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogPanel +import com.intellij.openapi.ui.validation.validationErrorIf +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.Placeholder +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.textValidation +import com.intellij.util.application +import com.intellij.util.io.readText +import java.nio.file.Path +import javax.swing.JComponent +import kotlin.collections.set +import kotlin.io.path.absolute +import kotlin.io.path.exists +import kotlinx.coroutines.Dispatchers + +/** + * The step to select a custom template repo. + */ +class CustomPlatformStep( + parent: PlatformTypeStep, +) : AbstractLongRunningAssetsStep(parent) { + + override val description: String = MCDevBundle("creator.ui.custom.step.description") + + val pathProperty = propertyGraph.property("").apply { + bindStorage("${javaClass.name}.path") + } + var path by pathProperty + + val descriptorProperty = propertyGraph.property(null) + var descriptor by descriptorProperty + + private val templateProperties = mutableMapOf Any>() + + override fun setupUI(builder: Panel) { + var taskParentComponent: JComponent? = null + builder.row(MCDevBundle("creator.ui.custom.path.label")) { + val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor().apply { + description = MCDevBundle("creator.ui.custom.path.dialog.description") + } + textFieldWithBrowseButton( + MCDevBundle("creator.ui.custom.path.dialog.title"), + context.project, + pathChooserDescriptor + ).align(AlignX.FILL) + .columns(COLUMNS_LARGE) + .bindText(pathProperty) + .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_directory")) { value -> + val file = kotlin.runCatching { + VirtualFileManager.getInstance().findFileByNioPath(Path.of(value)) + }.getOrNull() + file == null || !file.isDirectory + }) + .also { taskParentComponent = it.component } + } + + builder.row { + val placeholder = placeholder().align(AlignX.FILL) + createOptionsPanelInBackground(path, placeholder, taskParentComponent) + pathProperty.afterChange { path -> + createOptionsPanelInBackground(path, placeholder, taskParentComponent) + } + } + } + + private fun createOptionsPanelInBackground(path: String, placeholder: Placeholder, taskParentComponent: JComponent?) { + val task = object : Task.WithResult( + context.project, + taskParentComponent, + MCDevBundle("creator.step.generic.project_created.message"), + false + ) { + + override fun compute(indicator: ProgressIndicator): DialogPanel? { + if (project?.isDisposed == true) { + return null + } + + return doCreateOptionsPanel(path) + } + } + + placeholder.component = ProgressManager.getInstance().run(task) + } + + private fun doCreateOptionsPanel(path: String): DialogPanel? { + templateProperties.clear() + descriptor = null + val templateDescriptorPath = Path.of(path, ".mcdev.template.json") + if (!templateDescriptorPath.exists()) { + return null + } + + return panel { + val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) + descriptor = templateDescriptor + for (prop in templateDescriptor.properties) { + makeField(prop) + } + } + } + + private fun Panel.makeField(prop: TemplateProperty) { + when (prop.type) { + "class_fqn" -> { + val graphProp = propertyGraph.property("") + .bindStorage(makeStorageKey(prop)) + templateProperties[prop.name] = { ClassFqn(graphProp.get()) } + + row(MCDevBundle("creator.ui.custom.property.${prop.type}.label")) { + textField().bindText(graphProp).columns(COLUMNS_LARGE) + } + } + + "boolean" -> { + val graphProp = propertyGraph.property(prop.default as? Boolean ?: false) + .bindBooleanStorage(makeStorageKey(prop)) + templateProperties[prop.name] = { graphProp.get() } + + row(prop.label) { + checkBox("").bindSelected(graphProp) + } + } + + "dropdown" -> { + val defaultIndex = prop.default as? Int + val defaultValue = defaultIndex?.let { prop.options.getOrNull(it) } ?: prop.options.first() + val graphProp: ObservableMutableProperty = propertyGraph.property(defaultValue) + + if (prop.options.all { it is String }) { + graphProp.toStringProperty { it }.bindStorage(makeStorageKey(prop)) + } + + templateProperties[prop.name] = { graphProp.get() } + + row(prop.label) { + comboBox(prop.options).bindItem(graphProp) + } + } + + else -> thisLogger().error("Unknown template property type ${prop.type}") + } + } + + private fun makeStorageKey(prop: TemplateProperty) = + "${CustomPlatformStep::class.java.name}.property.${prop.name}.${prop.type}" + + override fun setupAssets(project: Project) { + val descriptor = descriptor!! + + val rootPath = Path.of(path).absolute() + + val baseData = data.getUserData(NewProjectWizardBaseData.KEY) ?: return + val javaVersion = findStep().preferredJdk.ordinal + val buildSystemProps = findStep>() + + assets.addTemplateProperties( + "PROJECT_NAME" to baseData.name, + "JAVA_VERSION" to javaVersion, + "GROUP_ID" to buildSystemProps.groupId, + "ARTIFACT_ID" to buildSystemProps.artifactId, + "VERSION" to buildSystemProps.version, + ) + + templateProperties.mapValuesTo(assets.templateProperties) { (_, property) -> property() } + + thisLogger().debug("Template properties: $templateProperties") + + for (file in descriptor.files) { + if (file.condition != null && + TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } + ) { + continue + } + + val relativeTemplate = TemplateEvaluator.template(assets.templateProperties, file.template).getOrNull() + ?: continue + val templatePath = rootPath.resolve(relativeTemplate).toAbsolutePath() + if (!templatePath.startsWith(rootPath)) { + continue + } + + val relativeDest = TemplateEvaluator.template(assets.templateProperties, file.destination).getOrNull() + ?: continue + val destPath = rootPath.resolve(relativeDest).toAbsolutePath() + if (!destPath.startsWith(rootPath)) { + // We want to make sure template files aren't 'escaping' the project directory + continue + } + + val fileName = destPath.fileName.toString().removeSuffix(".ft") + val baseFileName = FileUtilRt.getNameWithoutExtension(fileName) + val extension = FileUtilRt.getExtension(fileName) + val template = CustomFileTemplate(baseFileName, extension) + template.text = templatePath.readText() + assets.addAssets(GeneratorTemplateFile(rootPath.relativize(destPath).toString(), template)) + } + } + + class TypeFactory : PlatformTypeStep.Factory { + override val name + get() = MCDevBundle("creator.ui.platform.custom.name") + + override fun createStep(parent: PlatformTypeStep) = CustomPlatformStep(parent) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 5be203414..ed9932681 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -85,6 +85,7 @@ + @@ -1232,5 +1233,8 @@ description="Copy the reference to clipboard in Access Widener format"> + diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 7ab481b10..78d171568 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -29,9 +29,18 @@ creator.ui.group.version=Version: creator.ui.platform.type.label=Platform Type: creator.ui.platform.label=Platform: +creator.ui.platform.custom.name=Custom creator.ui.platform.mod.name=Mod creator.ui.platform.plugin.name=Plugin +creator.ui.custom.step.description=Creating project based on template... +creator.ui.custom.path.label=Templates Path: +creator.ui.custom.path.dialog.title=Template Root +creator.ui.custom.path.dialog.description=Select the root directory of the template repository + +# Used indirectly in the creator's procedural form +creator.ui.custom.property.class_fqn.label=Class Name: + creator.ui.license.label=License: creator.ui.main_class.label=Main Class: creator.ui.mc_version.label=Minecraft Version: @@ -72,6 +81,8 @@ creator.step.maven.import_maven.description=Importing Maven project creator.step.reformat.description=Reformatting files +creator.validation.custom.path_not_a_directory=Path is not a directory + creator.validation.group_id_non_example=Group ID must be changed from "org.example" creator.validation.semantic_version=Version must be a valid semantic version From e1c1dc5999dd4045c54fdcd32de675e5a12b4b1e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 3 May 2024 19:52:09 +0200 Subject: [PATCH 002/118] Add remember, editable and property derivation --- .../creator/custom/DerivationMethods.kt | 29 +++++++++ .../creator/custom/TemplateDescriptor.kt | 16 ++++- .../platformtype/CustomPlatformStep.kt | 63 ++++++++++++++++--- 3 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/creator/custom/DerivationMethods.kt diff --git a/src/main/kotlin/creator/custom/DerivationMethods.kt b/src/main/kotlin/creator/custom/DerivationMethods.kt new file mode 100644 index 000000000..2e141c743 --- /dev/null +++ b/src/main/kotlin/creator/custom/DerivationMethods.kt @@ -0,0 +1,29 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.util.SemanticVersion + +object DerivationMethods { + + val methods: Map Any?> = mapOf( + "extractVersionMajorMinor" to { extractVersionMajorMinor(it as? String) } + ) + + fun extractVersionMajorMinor(versionString: String?): SemanticVersion? { + if (versionString == null) { + return null + } + + val version = SemanticVersion.parse(versionString) + if (version.parts.size < 2) { + return null + } + + val (part1, part2) = version.parts + if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && + part2 is SemanticVersion.Companion.VersionPart.ReleasePart) { + return SemanticVersion(listOf(part1, part2)) + } + + return null + } +} diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 83986d0ec..4e1c6345b 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -2,7 +2,7 @@ package com.demonwav.mcdev.creator.custom data class TemplateDescriptor( val properties: List, - val files: List + val files: List, ) data class TemplateProperty( @@ -10,11 +10,21 @@ data class TemplateProperty( val type: String, val label: String, val options: List, - val default: Any + val remember: Boolean?, + val editable: Boolean?, + val default: Any, + val derives: PropertyDerivation?, +) + +data class PropertyDerivation( + val from: String, + val method: String, + val default: Any, + val whenModified: Boolean?, ) data class TemplateFile( val template: String, val destination: String, - val condition: String? = null + val condition: String? = null, ) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 70954ff85..9a26cb197 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep +import com.demonwav.mcdev.creator.custom.DerivationMethods import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplateProperty @@ -36,6 +37,7 @@ import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.observable.util.bindBooleanStorage import com.intellij.openapi.observable.util.bindStorage @@ -43,7 +45,6 @@ import com.intellij.openapi.observable.util.toStringProperty import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task -import com.intellij.openapi.progress.withBackgroundProgress import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.validation.validationErrorIf @@ -59,14 +60,12 @@ import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation -import com.intellij.util.application import com.intellij.util.io.readText import java.nio.file.Path import javax.swing.JComponent import kotlin.collections.set import kotlin.io.path.absolute import kotlin.io.path.exists -import kotlinx.coroutines.Dispatchers /** * The step to select a custom template repo. @@ -85,6 +84,7 @@ class CustomPlatformStep( val descriptorProperty = propertyGraph.property(null) var descriptor by descriptorProperty + private val graphProperties = mutableMapOf>() private val templateProperties = mutableMapOf Any>() override fun setupUI(builder: Panel) { @@ -159,37 +159,80 @@ class CustomPlatformStep( when (prop.type) { "class_fqn" -> { val graphProp = propertyGraph.property("") - .bindStorage(makeStorageKey(prop)) + if (prop.remember == true) { + graphProp.bindStorage(makeStorageKey(prop)) + } + + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { ClassFqn(graphProp.get()) } row(MCDevBundle("creator.ui.custom.property.${prop.type}.label")) { - textField().bindText(graphProp).columns(COLUMNS_LARGE) + textField().bindText(graphProp).columns(COLUMNS_LARGE).enabled(prop.editable != false) } } "boolean" -> { val graphProp = propertyGraph.property(prop.default as? Boolean ?: false) - .bindBooleanStorage(makeStorageKey(prop)) + if (prop.remember == true) { + graphProp.bindBooleanStorage(makeStorageKey(prop)) + } + + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { graphProp.get() } row(prop.label) { - checkBox("").bindSelected(graphProp) + checkBox("").bindSelected(graphProp).enabled(prop.editable != false) } } "dropdown" -> { val defaultIndex = prop.default as? Int val defaultValue = defaultIndex?.let { prop.options.getOrNull(it) } ?: prop.options.first() - val graphProp: ObservableMutableProperty = propertyGraph.property(defaultValue) + val graphProp = propertyGraph.property(defaultValue) - if (prop.options.all { it is String }) { + if (prop.remember == true && prop.options.all { it is String }) { graphProp.toStringProperty { it }.bindStorage(makeStorageKey(prop)) } + graphProperties[prop.name] = graphProp + templateProperties[prop.name] = { graphProp.get() } + + row(prop.label) { + comboBox(prop.options).bindItem(graphProp).enabled(prop.editable != false) + } + } + + "textfield" -> { + val graphProp = propertyGraph.property(prop.default as? String ?: "") + if (prop.remember == true) { + graphProp.bindStorage(makeStorageKey(prop)) + } + + if (prop.derives != null) { + val parentProperty = graphProperties[prop.derives.from] + if (parentProperty == null) { + thisLogger().error("Unknown parent property '${prop.derives.from}'") + return + } + + val method = DerivationMethods.methods[prop.derives.method] + if (method == null) { + thisLogger().error("Unknown derivation method '${prop.derives.method}'") + return + } + + graphProp.set(method(parentProperty.get())?.toString() ?: prop.derives.default as String) + + graphProp.dependsOn(parentProperty, prop.derives.whenModified != false) { + method(parentProperty.get())?.toString() ?: prop.derives.default as String + } + } + + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { graphProp.get() } row(prop.label) { - comboBox(prop.options).bindItem(graphProp) + textField().bindText(graphProp).enabled(prop.editable != false) } } From d56284aa97089b956fd379f0289dc803c08f6476 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 3 May 2024 20:20:02 +0200 Subject: [PATCH 003/118] Add hidden properties --- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 1 + .../kotlin/creator/platformtype/CustomPlatformStep.kt | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 4e1c6345b..b81f24897 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -11,6 +11,7 @@ data class TemplateProperty( val label: String, val options: List, val remember: Boolean?, + val hidden: Boolean?, val editable: Boolean?, val default: Any, val derives: PropertyDerivation?, diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 9a26cb197..089c9dcaa 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -168,7 +168,7 @@ class CustomPlatformStep( row(MCDevBundle("creator.ui.custom.property.${prop.type}.label")) { textField().bindText(graphProp).columns(COLUMNS_LARGE).enabled(prop.editable != false) - } + }.visible(prop.hidden != true) } "boolean" -> { @@ -182,7 +182,7 @@ class CustomPlatformStep( row(prop.label) { checkBox("").bindSelected(graphProp).enabled(prop.editable != false) - } + }.visible(prop.hidden != true) } "dropdown" -> { @@ -199,7 +199,7 @@ class CustomPlatformStep( row(prop.label) { comboBox(prop.options).bindItem(graphProp).enabled(prop.editable != false) - } + }.visible(prop.hidden != true) } "textfield" -> { @@ -233,7 +233,7 @@ class CustomPlatformStep( row(prop.label) { textField().bindText(graphProp).enabled(prop.editable != false) - } + }.visible(prop.hidden != true) } else -> thisLogger().error("Unknown template property type ${prop.type}") From 2e791189a6f1dff46064b831eae32154877a8cd5 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 4 May 2024 01:48:50 +0200 Subject: [PATCH 004/118] Implement property derivation for all types --- .../creator/custom/DerivationMethods.kt | 15 ++-- .../creator/custom/TemplateDescriptor.kt | 1 + .../platformtype/CustomPlatformStep.kt | 69 ++++++++++++------- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/creator/custom/DerivationMethods.kt b/src/main/kotlin/creator/custom/DerivationMethods.kt index 2e141c743..d6d0d0fde 100644 --- a/src/main/kotlin/creator/custom/DerivationMethods.kt +++ b/src/main/kotlin/creator/custom/DerivationMethods.kt @@ -2,25 +2,28 @@ package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.util.SemanticVersion +typealias DerivationMethod = (from: Any?, properties: Map, parameters: Map) -> Any? + object DerivationMethods { - val methods: Map Any?> = mapOf( - "extractVersionMajorMinor" to { extractVersionMajorMinor(it as? String) } + val methods: Map = mapOf( + "extractVersionMajorMinor" to ::extractVersionMajorMinor ) - fun extractVersionMajorMinor(versionString: String?): SemanticVersion? { - if (versionString == null) { + fun extractVersionMajorMinor(from: Any?, properties: Map, parameters: Map): SemanticVersion? { + if (from !is String) { return null } - val version = SemanticVersion.parse(versionString) + val version = SemanticVersion.parse(from) if (version.parts.size < 2) { return null } val (part1, part2) = version.parts if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && - part2 is SemanticVersion.Companion.VersionPart.ReleasePart) { + part2 is SemanticVersion.Companion.VersionPart.ReleasePart + ) { return SemanticVersion(listOf(part1, part2)) } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index b81f24897..db6163cce 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -22,6 +22,7 @@ data class PropertyDerivation( val method: String, val default: Any, val whenModified: Boolean?, + val parameters: Map?, ) data class TemplateFile( diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 089c9dcaa..36719f8ed 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep import com.demonwav.mcdev.creator.custom.DerivationMethods +import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplateProperty @@ -38,7 +39,6 @@ import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.observable.util.bindBooleanStorage import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.observable.util.toStringProperty @@ -163,6 +163,8 @@ class CustomPlatformStep( graphProp.bindStorage(makeStorageKey(prop)) } + setupPropertyDerivation(graphProp, prop.derives) { it?.toString() ?: "" } + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { ClassFqn(graphProp.get()) } @@ -172,16 +174,19 @@ class CustomPlatformStep( } "boolean" -> { - val graphProp = propertyGraph.property(prop.default as? Boolean ?: false) + val defaultValue = prop.default as? Boolean ?: false + val graphProp = propertyGraph.property(defaultValue) if (prop.remember == true) { graphProp.bindBooleanStorage(makeStorageKey(prop)) } + setupPropertyDerivation(graphProp, prop.derives) { it as? Boolean ?: defaultValue } + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { graphProp.get() } row(prop.label) { - checkBox("").bindSelected(graphProp).enabled(prop.editable != false) + checkBox(prop.label).bindSelected(graphProp).enabled(prop.editable != false) }.visible(prop.hidden != true) } @@ -194,6 +199,10 @@ class CustomPlatformStep( graphProp.toStringProperty { it }.bindStorage(makeStorageKey(prop)) } + setupPropertyDerivation(graphProp, prop.derives) { + it?.toString()?.takeIf(prop.options::contains) ?: defaultValue + } + graphProperties[prop.name] = graphProp templateProperties[prop.name] = { graphProp.get() } @@ -208,25 +217,7 @@ class CustomPlatformStep( graphProp.bindStorage(makeStorageKey(prop)) } - if (prop.derives != null) { - val parentProperty = graphProperties[prop.derives.from] - if (parentProperty == null) { - thisLogger().error("Unknown parent property '${prop.derives.from}'") - return - } - - val method = DerivationMethods.methods[prop.derives.method] - if (method == null) { - thisLogger().error("Unknown derivation method '${prop.derives.method}'") - return - } - - graphProp.set(method(parentProperty.get())?.toString() ?: prop.derives.default as String) - - graphProp.dependsOn(parentProperty, prop.derives.whenModified != false) { - method(parentProperty.get())?.toString() ?: prop.derives.default as String - } - } + setupPropertyDerivation(graphProp, prop.derives) { it?.toString() ?: "" } graphProperties[prop.name] = graphProp templateProperties[prop.name] = { graphProp.get() } @@ -260,7 +251,7 @@ class CustomPlatformStep( "VERSION" to buildSystemProps.version, ) - templateProperties.mapValuesTo(assets.templateProperties) { (_, property) -> property() } + collectTemplateProperties(assets.templateProperties) thisLogger().debug("Template properties: $templateProperties") @@ -295,6 +286,38 @@ class CustomPlatformStep( } } + private fun collectTemplateProperties(into: MutableMap = mutableMapOf()) = + templateProperties.mapValuesTo(into) { (_, property) -> property() } + + private fun callDerivationMethod(derivation: PropertyDerivation): Any? { + val method = DerivationMethods.methods[derivation.method] + if (method == null) { + thisLogger().error("Unknown derivation method '${derivation.method}'") + return null + } + + val properties = collectTemplateProperties() + val parameters = derivation.parameters ?: mapOf() + return method(properties[derivation.from], properties, parameters) ?: derivation.default + } + + private fun setupPropertyDerivation(graphProperty: GraphProperty, derivation: PropertyDerivation?, transform: (Any?) -> T) { + if (derivation == null) { + return + } + + val parentProperty = graphProperties[derivation.from] + if (parentProperty == null) { + thisLogger().error("Unknown parent property '${derivation.from}'") + return + } + + graphProperty.set(transform(callDerivationMethod(derivation))) + graphProperty.dependsOn(parentProperty, derivation.whenModified != false) { + transform(callDerivationMethod(derivation)) + } + } + class TypeFactory : PlatformTypeStep.Factory { override val name get() = MCDevBundle("creator.ui.platform.custom.name") From 1486196f11d807def7eed47d2d9663aa0ff811f1 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 4 May 2024 18:07:43 +0200 Subject: [PATCH 005/118] Actual types implementation Also fix template condition evaluation --- .../buildsystem/AbstractBuildSystemStep.kt | 2 +- .../creator/custom/TemplateDescriptor.kt | 2 +- .../creator/custom/TemplateEvaluator.kt | 4 +- .../custom/types/BooleanPropertyType.kt | 23 +++ .../custom/types/ClassFqnPropertyType.kt | 26 +++ .../creator/custom/types/PropertyType.kt | 45 +++++ .../types/SemanticVersionPropertyType.kt | 61 +++++++ .../custom/types/StringPropertyType.kt | 29 +++ .../creator/platformtype/CreatorProperty.kt | 6 + .../platformtype/CustomPlatformStep.kt | 167 ++++++------------ 10 files changed, 250 insertions(+), 115 deletions(-) create mode 100644 src/main/kotlin/creator/custom/types/BooleanPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/PropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/StringPropertyType.kt create mode 100644 src/main/kotlin/creator/platformtype/CreatorProperty.kt diff --git a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt index 51614b1fb..4e7ff3e37 100644 --- a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt +++ b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt @@ -37,7 +37,7 @@ abstract class AbstractBuildSystemStep( parent: NewProjectWizardStep, ) : AbstractNewProjectWizardMultiStep(parent, EP_NAME) { companion object { - private val PLATFORM_NAME_KEY = Key.create("mcdev.platformName") + val PLATFORM_NAME_KEY = Key.create("mcdev.platformName") val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.buildSystemWizard") } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index db6163cce..51b112bc2 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -9,7 +9,7 @@ data class TemplateProperty( val name: String, val type: String, val label: String, - val options: List, + val options: List?, val remember: Boolean?, val hidden: Boolean?, val editable: Boolean?, diff --git a/src/main/kotlin/creator/custom/TemplateEvaluator.kt b/src/main/kotlin/creator/custom/TemplateEvaluator.kt index 69cdcca8d..620bb2e73 100644 --- a/src/main/kotlin/creator/custom/TemplateEvaluator.kt +++ b/src/main/kotlin/creator/custom/TemplateEvaluator.kt @@ -19,7 +19,7 @@ object TemplateEvaluator { } fun condition(properties: Map, condition: String): Result { - val actualCondition = "#if ($condition) true#else false#end" - return evaluate(properties, actualCondition).map { it.second.toBoolean() } + val actualCondition = "#if ($condition) true #else false #end" + return evaluate(properties, actualCondition).map { it.second.trim().toBoolean() } } } diff --git a/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt b/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt new file mode 100644 index 000000000..c555298ab --- /dev/null +++ b/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt @@ -0,0 +1,23 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindSelected + +class BooleanPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): Boolean = raw as? Boolean ?: false + + override fun serialize(value: Boolean): String = value.toString() + + override fun deserialize(string: String): Boolean = string.toBoolean() + + override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + row(property.label) { + checkBox(property.label) + .bindSelected(graphProperty) + .enabled(property.editable != false) + }.visible(property.hidden != false) + } +} diff --git a/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt b/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt new file mode 100644 index 000000000..1aee6a09a --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt @@ -0,0 +1,26 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class ClassFqnPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): ClassFqn = ClassFqn(raw as? String ?: "") + + override fun serialize(value: ClassFqn): String = value.toString() + + override fun deserialize(string: String): ClassFqn = ClassFqn(string) + + override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + row(property.label) { + textField().bindText(toStringProperty(graphProperty)) + .columns(COLUMNS_LARGE) + .enabled(property.editable != false) + }.visible(property.hidden != false) + } +} diff --git a/src/main/kotlin/creator/custom/types/PropertyType.kt b/src/main/kotlin/creator/custom/types/PropertyType.kt new file mode 100644 index 000000000..0f1c97a2b --- /dev/null +++ b/src/main/kotlin/creator/custom/types/PropertyType.kt @@ -0,0 +1,45 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.platformtype.CreatorProperty +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.ObservableMutableProperty +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.dsl.builder.Panel + +interface PropertyType { + + fun createDefaultValue(raw: Any?): T + + fun serialize(value: T): String + + fun deserialize(string: String): T + + fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = + graphProperty.transform(::serialize, ::deserialize) + + /** + * Produces a new value based on the provided [parent] property value and the template-defined [derivation] configuration. + * + * You must **NOT** [set][GraphProperty.set] the value of [property] in the process. You may however [get][GraphProperty.get] it at will. + * + * @param property the property depending on [parent] + * @param parent the GraphProperty and PropertyType this [property] depends on + * @param derivation the configuration of the desired derivation + * + * @see GraphProperty.dependsOn + */ + fun derive( + property: GraphProperty, + parent: CreatorProperty<*>, + properties: Map>, + derivation: PropertyDerivation + ): T { + thisLogger().error("This type doesn't support derivation") + return property.get() + } + + fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) +} diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt b/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt new file mode 100644 index 000000000..ca983381e --- /dev/null +++ b/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt @@ -0,0 +1,61 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.platformtype.CreatorProperty +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.ui.dsl.builder.COLUMNS_SHORT +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class SemanticVersionPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): SemanticVersion = + SemanticVersion.tryParse(raw as? String ?: "") ?: SemanticVersion(emptyList()) + + override fun serialize(value: SemanticVersion): String = value.toString() + + override fun deserialize(string: String): SemanticVersion = + SemanticVersion.tryParse(string) ?: SemanticVersion(emptyList()) + + override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + row(property.label) { + textField().bindText(toStringProperty(graphProperty)) + .columns(COLUMNS_SHORT) + .enabled(property.editable != false) + }.visible(property.hidden != false) + } + + override fun derive( + property: GraphProperty, + parent: CreatorProperty<*>, + properties: Map>, + derivation: PropertyDerivation + ): SemanticVersion { + return when (derivation.method) { + "extractVersionMajorMinor" -> extractVersionMajorMinor(parent.graphProperty.get()) + else -> throw IllegalArgumentException("Unknown method derivation $derivation") + } + } + + private fun extractVersionMajorMinor(from: Any?): SemanticVersion { + if (from !is SemanticVersion) { + return SemanticVersion(emptyList()) + } + + if (from.parts.size < 2) { + return SemanticVersion(emptyList()) + } + + val (part1, part2) = from.parts + if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && + part2 is SemanticVersion.Companion.VersionPart.ReleasePart + ) { + return SemanticVersion(listOf(part1, part2)) + } + + return SemanticVersion(emptyList()) + } +} diff --git a/src/main/kotlin/creator/custom/types/StringPropertyType.kt b/src/main/kotlin/creator/custom/types/StringPropertyType.kt new file mode 100644 index 000000000..30380afaa --- /dev/null +++ b/src/main/kotlin/creator/custom/types/StringPropertyType.kt @@ -0,0 +1,29 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.ObservableMutableProperty +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class StringPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" + + override fun serialize(value: String): String = value + + override fun deserialize(string: String): String = string + + override fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = + graphProperty + + override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + row(property.label) { + textField().bindText(toStringProperty(graphProperty)) + .columns(COLUMNS_LARGE) + .enabled(property.editable != false) + }.visible(property.hidden != false) + } +} diff --git a/src/main/kotlin/creator/platformtype/CreatorProperty.kt b/src/main/kotlin/creator/platformtype/CreatorProperty.kt new file mode 100644 index 000000000..fc55fef37 --- /dev/null +++ b/src/main/kotlin/creator/platformtype/CreatorProperty.kt @@ -0,0 +1,6 @@ +package com.demonwav.mcdev.creator.platformtype + +import com.demonwav.mcdev.creator.custom.types.PropertyType +import com.intellij.openapi.observable.properties.GraphProperty + +data class CreatorProperty(val graphProperty: GraphProperty, val type: PropertyType) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 36719f8ed..7e8fb90e4 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -22,13 +22,16 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer +import com.demonwav.mcdev.creator.buildsystem.AbstractBuildSystemStep.Companion.PLATFORM_NAME_KEY import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep -import com.demonwav.mcdev.creator.custom.DerivationMethods -import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.demonwav.mcdev.creator.custom.types.BooleanPropertyType +import com.demonwav.mcdev.creator.custom.types.ClassFqnPropertyType +import com.demonwav.mcdev.creator.custom.types.PropertyType +import com.demonwav.mcdev.creator.custom.types.SemanticVersionPropertyType +import com.demonwav.mcdev.creator.custom.types.StringPropertyType import com.demonwav.mcdev.creator.findStep import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep import com.demonwav.mcdev.util.fromJson @@ -38,10 +41,7 @@ import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.util.bindBooleanStorage import com.intellij.openapi.observable.util.bindStorage -import com.intellij.openapi.observable.util.toStringProperty import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task @@ -50,12 +50,12 @@ import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder import com.intellij.ui.dsl.builder.bindItem -import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel @@ -63,7 +63,6 @@ import com.intellij.ui.dsl.builder.textValidation import com.intellij.util.io.readText import java.nio.file.Path import javax.swing.JComponent -import kotlin.collections.set import kotlin.io.path.absolute import kotlin.io.path.exists @@ -84,8 +83,7 @@ class CustomPlatformStep( val descriptorProperty = propertyGraph.property(null) var descriptor by descriptorProperty - private val graphProperties = mutableMapOf>() - private val templateProperties = mutableMapOf Any>() + private val properties = mutableMapOf>() override fun setupUI(builder: Panel) { var taskParentComponent: JComponent? = null @@ -118,7 +116,11 @@ class CustomPlatformStep( } } - private fun createOptionsPanelInBackground(path: String, placeholder: Placeholder, taskParentComponent: JComponent?) { + private fun createOptionsPanelInBackground( + path: String, + placeholder: Placeholder, + taskParentComponent: JComponent? + ) { val task = object : Task.WithResult( context.project, taskParentComponent, @@ -139,7 +141,7 @@ class CustomPlatformStep( } private fun doCreateOptionsPanel(path: String): DialogPanel? { - templateProperties.clear() + properties.clear() descriptor = null val templateDescriptorPath = Path.of(path, ".mcdev.template.json") if (!templateDescriptorPath.exists()) { @@ -156,79 +158,51 @@ class CustomPlatformStep( } private fun Panel.makeField(prop: TemplateProperty) { - when (prop.type) { - "class_fqn" -> { - val graphProp = propertyGraph.property("") - if (prop.remember == true) { - graphProp.bindStorage(makeStorageKey(prop)) - } - - setupPropertyDerivation(graphProp, prop.derives) { it?.toString() ?: "" } - - graphProperties[prop.name] = graphProp - templateProperties[prop.name] = { ClassFqn(graphProp.get()) } - - row(MCDevBundle("creator.ui.custom.property.${prop.type}.label")) { - textField().bindText(graphProp).columns(COLUMNS_LARGE).enabled(prop.editable != false) - }.visible(prop.hidden != true) - } - - "boolean" -> { - val defaultValue = prop.default as? Boolean ?: false - val graphProp = propertyGraph.property(defaultValue) - if (prop.remember == true) { - graphProp.bindBooleanStorage(makeStorageKey(prop)) - } - - setupPropertyDerivation(graphProp, prop.derives) { it as? Boolean ?: defaultValue } - - graphProperties[prop.name] = graphProp - templateProperties[prop.name] = { graphProp.get() } - - row(prop.label) { - checkBox(prop.label).bindSelected(graphProp).enabled(prop.editable != false) - }.visible(prop.hidden != true) - } - - "dropdown" -> { - val defaultIndex = prop.default as? Int - val defaultValue = defaultIndex?.let { prop.options.getOrNull(it) } ?: prop.options.first() - val graphProp = propertyGraph.property(defaultValue) - - if (prop.remember == true && prop.options.all { it is String }) { - graphProp.toStringProperty { it }.bindStorage(makeStorageKey(prop)) - } - - setupPropertyDerivation(graphProp, prop.derives) { - it?.toString()?.takeIf(prop.options::contains) ?: defaultValue - } - - graphProperties[prop.name] = graphProp - templateProperties[prop.name] = { graphProp.get() } - - row(prop.label) { - comboBox(prop.options).bindItem(graphProp).enabled(prop.editable != false) - }.visible(prop.hidden != true) - } - - "textfield" -> { - val graphProp = propertyGraph.property(prop.default as? String ?: "") - if (prop.remember == true) { - graphProp.bindStorage(makeStorageKey(prop)) - } + if (prop.name in properties.keys) { + return thisLogger().error("Duplicate property name ${prop.name}") + } - setupPropertyDerivation(graphProp, prop.derives) { it?.toString() ?: "" } + @Suppress("UNCHECKED_CAST") + val type = when (prop.type) { + "string" -> StringPropertyType() + "boolean" -> BooleanPropertyType() + "class_fqn" -> ClassFqnPropertyType() + "semantic_version" -> SemanticVersionPropertyType() + else -> return thisLogger().error("Unknown template property type ${prop.type}") + } as PropertyType + + val isDropdown = !prop.options.isNullOrEmpty() + val options = prop.options?.filterIsInstance()?.map(type::deserialize) ?: emptyList() + val defaultOptionIndex = if (isDropdown) prop.default as? Int ?: 0 else null + val defaultValue = type.createDefaultValue(if (isDropdown) prop.options!![defaultOptionIndex!!] else prop.default) + val graphProp = propertyGraph.property(defaultValue) + + if (prop.remember != false && prop.derives == null) { + type.toStringProperty(graphProp).bindStorage(makeStorageKey(prop)) + } - graphProperties[prop.name] = graphProp - templateProperties[prop.name] = { graphProp.get() } + if (prop.derives != null) { + val parentProperty = properties[prop.derives.from] + ?: return thisLogger().error("Unknown parent property '${prop.derives.from}'") - row(prop.label) { - textField().bindText(graphProp).enabled(prop.editable != false) - }.visible(prop.hidden != true) + graphProp.set(type.derive(graphProp, parentProperty, properties, prop.derives)) + graphProp.dependsOn(parentProperty.graphProperty, prop.derives.whenModified != false) { + type.derive(graphProp, parentProperty, properties, prop.derives) } + } - else -> thisLogger().error("Unknown template property type ${prop.type}") + if (isDropdown) { + row(prop.label) { + comboBox(options) + .bindItem(graphProp) + .enabled(prop.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.visible(prop.hidden != false) + } else { + with(type) { buildUi(graphProp, prop) } } + + properties[prop.name] = CreatorProperty(graphProp, type) } private fun makeStorageKey(prop: TemplateProperty) = @@ -253,11 +227,11 @@ class CustomPlatformStep( collectTemplateProperties(assets.templateProperties) - thisLogger().debug("Template properties: $templateProperties") + thisLogger().debug("Template properties: ${assets.templateProperties}") for (file in descriptor.files) { if (file.condition != null && - TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } + !TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } ) { continue } @@ -287,36 +261,7 @@ class CustomPlatformStep( } private fun collectTemplateProperties(into: MutableMap = mutableMapOf()) = - templateProperties.mapValuesTo(into) { (_, property) -> property() } - - private fun callDerivationMethod(derivation: PropertyDerivation): Any? { - val method = DerivationMethods.methods[derivation.method] - if (method == null) { - thisLogger().error("Unknown derivation method '${derivation.method}'") - return null - } - - val properties = collectTemplateProperties() - val parameters = derivation.parameters ?: mapOf() - return method(properties[derivation.from], properties, parameters) ?: derivation.default - } - - private fun setupPropertyDerivation(graphProperty: GraphProperty, derivation: PropertyDerivation?, transform: (Any?) -> T) { - if (derivation == null) { - return - } - - val parentProperty = graphProperties[derivation.from] - if (parentProperty == null) { - thisLogger().error("Unknown parent property '${derivation.from}'") - return - } - - graphProperty.set(transform(callDerivationMethod(derivation))) - graphProperty.dependsOn(parentProperty, derivation.whenModified != false) { - transform(callDerivationMethod(derivation)) - } - } + properties.mapValuesTo(into) { (_, prop) -> prop.graphProperty.get() } class TypeFactory : PlatformTypeStep.Factory { override val name From bf48447a4c5ee81395656e30fc6596630f6692ac Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 6 May 2024 20:20:16 +0200 Subject: [PATCH 006/118] Some more stuff --- .../ProjectSetupFinalizerWizardStep.kt | 5 +- .../creator/custom/TemplateDescriptor.kt | 3 +- .../kotlin/creator/custom/model/CreatorJdk.kt | 10 ++ .../custom/types/BooleanPropertyType.kt | 7 +- .../custom/types/ClassFqnPropertyType.kt | 25 ++- .../custom/types/IntegerPropertyType.kt | 27 +++ .../creator/custom/types/JdkPropertyType.kt | 32 ++++ .../creator/custom/types/PropertyType.kt | 16 +- .../types/SemanticVersionPropertyType.kt | 14 +- .../custom/types/StringPropertyType.kt | 3 +- .../creator/platformtype/CreatorProperty.kt | 4 +- .../platformtype/CustomPlatformStep.kt | 165 ++++++++++++------ 12 files changed, 241 insertions(+), 70 deletions(-) create mode 100644 src/main/kotlin/creator/custom/model/CreatorJdk.kt create mode 100644 src/main/kotlin/creator/custom/types/IntegerPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/JdkPropertyType.kt diff --git a/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt b/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt index 6aa8694bb..77c5a9740 100644 --- a/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt +++ b/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt @@ -28,6 +28,7 @@ import com.intellij.ide.wizard.AbstractNewProjectWizardStep import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.ObservableProperty import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdkVersion @@ -126,7 +127,9 @@ class JdkProjectSetupFinalizer( private var preferredJdkLabel: Placeholder? = null private var preferredJdkReason = MCDevBundle("creator.validation.jdk_preferred_default_reason") - var preferredJdk: JavaSdkVersion = JavaSdkVersion.JDK_17 + val preferredJdkProperty = propertyGraph.property(JavaSdkVersion.JDK_17) + + var preferredJdk: JavaSdkVersion by preferredJdkProperty private set fun setPreferredJdk(value: JavaSdkVersion, reason: String) { diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 51b112bc2..e9a8af5ac 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -15,10 +15,11 @@ data class TemplateProperty( val editable: Boolean?, val default: Any, val derives: PropertyDerivation?, + val inheritFrom: String? ) data class PropertyDerivation( - val from: String, + val parents: List?, val method: String, val default: Any, val whenModified: Boolean?, diff --git a/src/main/kotlin/creator/custom/model/CreatorJdk.kt b/src/main/kotlin/creator/custom/model/CreatorJdk.kt new file mode 100644 index 000000000..4bc17dd93 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/CreatorJdk.kt @@ -0,0 +1,10 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.intellij.openapi.projectRoots.JavaSdk +import com.intellij.openapi.projectRoots.Sdk + +data class CreatorJdk(val sdk: Sdk?) { + + val javaVersion: Int + get() = sdk?.let { JavaSdk.getInstance().getVersion(it)?.ordinal } ?: 17 +} diff --git a/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt b/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt index c555298ab..d528cd956 100644 --- a/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt +++ b/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt @@ -1,6 +1,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindSelected @@ -13,7 +14,11 @@ class BooleanPropertyType : PropertyType { override fun deserialize(string: String): Boolean = string.toBoolean() - override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + override fun Panel.buildUi( + context: WizardContext, + graphProperty: GraphProperty, + property: TemplateProperty + ) { row(property.label) { checkBox(property.label) .bindSelected(graphProperty) diff --git a/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt b/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt index 1aee6a09a..f4b013888 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt @@ -1,7 +1,9 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateProperty import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel @@ -16,11 +18,32 @@ class ClassFqnPropertyType : PropertyType { override fun deserialize(string: String): ClassFqn = ClassFqn(string) - override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + override fun Panel.buildUi( + context: WizardContext, + graphProperty: GraphProperty, + property: TemplateProperty + ) { row(property.label) { textField().bindText(toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(property.editable != false) }.visible(property.hidden != false) } + + override fun derive( + property: GraphProperty, + parentValues: List, + properties: Map, + derivation: PropertyDerivation + ): ClassFqn { + return when (derivation.method) { + "suggestClassName" -> suggestClassName(parentValues) + else -> throw IllegalArgumentException("Unknown method derivation $derivation") + } + } + + private fun suggestClassName(parentValues: List): ClassFqn { + val (groupId, name) = parentValues + return ClassFqn("$groupId.$name.$name") + } } diff --git a/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt b/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt new file mode 100644 index 000000000..afe2b4cbd --- /dev/null +++ b/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt @@ -0,0 +1,27 @@ +package com.demonwav.mcdev.creator.custom.types.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.custom.types.PropertyType +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindIntText +import com.intellij.ui.dsl.builder.columns + +class IntegerPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 + + override fun serialize(value: Int): String = value.toString() + + override fun deserialize(string: String): Int = string.toIntOrNull() ?: 0 + + override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { + row(property.label) { + intTextField().bindIntText(graphProperty) + .columns(COLUMNS_LARGE) + .enabled(property.editable != false) + }.visible(property.hidden != false) + } +} diff --git a/src/main/kotlin/creator/custom/types/JdkPropertyType.kt b/src/main/kotlin/creator/custom/types/JdkPropertyType.kt new file mode 100644 index 000000000..81735860f --- /dev/null +++ b/src/main/kotlin/creator/custom/types/JdkPropertyType.kt @@ -0,0 +1,32 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.JdkComboBoxWithPreference +import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.custom.model.CreatorJdk +import com.demonwav.mcdev.creator.jdkComboBoxWithPreference +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.util.transform +import com.intellij.openapi.projectRoots.JavaSdkVersion +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.ui.dsl.builder.Panel + +class JdkPropertyType : PropertyType { + + override fun createDefaultValue(raw: Any?): CreatorJdk = CreatorJdk(null) + + override fun serialize(value: CreatorJdk): String = value.sdk?.homePath ?: "" + + override fun deserialize(string: String): CreatorJdk = + CreatorJdk(ProjectJdkTable.getInstance().allJdks.find { it.homePath == string }) + + override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { + val preferredVersion = property.default as? Int ?: 17 + lateinit var jdkComboBox: JdkComboBoxWithPreference + row(property.label) { + val sdkProperty = graphProperty.transform(CreatorJdk::sdk, ::CreatorJdk) + jdkComboBox = jdkComboBoxWithPreference(context, sdkProperty, property.name).component + jdkComboBox.setPreferredJdk(JavaSdkVersion.entries[preferredVersion]) + } + } +} diff --git a/src/main/kotlin/creator/custom/types/PropertyType.kt b/src/main/kotlin/creator/custom/types/PropertyType.kt index 0f1c97a2b..5ad1142e7 100644 --- a/src/main/kotlin/creator/custom/types/PropertyType.kt +++ b/src/main/kotlin/creator/custom/types/PropertyType.kt @@ -2,7 +2,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.platformtype.CreatorProperty +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty @@ -21,25 +21,25 @@ interface PropertyType { graphProperty.transform(::serialize, ::deserialize) /** - * Produces a new value based on the provided [parent] property value and the template-defined [derivation] configuration. + * Produces a new value based on the provided [parentValues] property value and the template-defined [derivation] configuration. * * You must **NOT** [set][GraphProperty.set] the value of [property] in the process. You may however [get][GraphProperty.get] it at will. * - * @param property the property depending on [parent] - * @param parent the GraphProperty and PropertyType this [property] depends on + * @param property the property depending on [parentValues] + * @param parentValues the GraphProperty and PropertyType this [property] depends on * @param derivation the configuration of the desired derivation * * @see GraphProperty.dependsOn */ fun derive( property: GraphProperty, - parent: CreatorProperty<*>, - properties: Map>, + parentValues: List, + properties: Map, derivation: PropertyDerivation - ): T { + ): Any? { thisLogger().error("This type doesn't support derivation") return property.get() } - fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) + fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) } diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt b/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt index ca983381e..34b984eb1 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt @@ -2,8 +2,8 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.platformtype.CreatorProperty import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.ui.dsl.builder.COLUMNS_SHORT import com.intellij.ui.dsl.builder.Panel @@ -20,7 +20,11 @@ class SemanticVersionPropertyType : PropertyType { override fun deserialize(string: String): SemanticVersion = SemanticVersion.tryParse(string) ?: SemanticVersion(emptyList()) - override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + override fun Panel.buildUi( + context: WizardContext, + graphProperty: GraphProperty, + property: TemplateProperty + ) { row(property.label) { textField().bindText(toStringProperty(graphProperty)) .columns(COLUMNS_SHORT) @@ -30,12 +34,12 @@ class SemanticVersionPropertyType : PropertyType { override fun derive( property: GraphProperty, - parent: CreatorProperty<*>, - properties: Map>, + parentValues: List, + properties: Map, derivation: PropertyDerivation ): SemanticVersion { return when (derivation.method) { - "extractVersionMajorMinor" -> extractVersionMajorMinor(parent.graphProperty.get()) + "extractVersionMajorMinor" -> extractVersionMajorMinor(parentValues[0]) else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } diff --git a/src/main/kotlin/creator/custom/types/StringPropertyType.kt b/src/main/kotlin/creator/custom/types/StringPropertyType.kt index 30380afaa..d11955985 100644 --- a/src/main/kotlin/creator/custom/types/StringPropertyType.kt +++ b/src/main/kotlin/creator/custom/types/StringPropertyType.kt @@ -1,6 +1,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.ui.dsl.builder.COLUMNS_LARGE @@ -19,7 +20,7 @@ class StringPropertyType : PropertyType { override fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = graphProperty - override fun Panel.buildUi(graphProperty: GraphProperty, property: TemplateProperty) { + override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { row(property.label) { textField().bindText(toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) diff --git a/src/main/kotlin/creator/platformtype/CreatorProperty.kt b/src/main/kotlin/creator/platformtype/CreatorProperty.kt index fc55fef37..b048903e0 100644 --- a/src/main/kotlin/creator/platformtype/CreatorProperty.kt +++ b/src/main/kotlin/creator/platformtype/CreatorProperty.kt @@ -1,6 +1,6 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.creator.custom.types.PropertyType -import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.ObservableProperty -data class CreatorProperty(val graphProperty: GraphProperty, val type: PropertyType) +data class CreatorProperty(val graphProperty: ObservableProperty, val type: PropertyType) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 7e8fb90e4..4defbe0ca 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -22,16 +22,17 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer -import com.demonwav.mcdev.creator.buildsystem.AbstractBuildSystemStep.Companion.PLATFORM_NAME_KEY import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplateProperty import com.demonwav.mcdev.creator.custom.types.BooleanPropertyType import com.demonwav.mcdev.creator.custom.types.ClassFqnPropertyType +import com.demonwav.mcdev.creator.custom.types.JdkPropertyType import com.demonwav.mcdev.creator.custom.types.PropertyType import com.demonwav.mcdev.creator.custom.types.SemanticVersionPropertyType import com.demonwav.mcdev.creator.custom.types.StringPropertyType +import com.demonwav.mcdev.creator.custom.types.creator.custom.types.IntegerPropertyType import com.demonwav.mcdev.creator.findStep import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep import com.demonwav.mcdev.util.fromJson @@ -46,7 +47,6 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.vfs.VirtualFileManager @@ -62,7 +62,22 @@ import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation import com.intellij.util.io.readText import java.nio.file.Path +import java.util.function.Consumer import javax.swing.JComponent +import kotlin.collections.List +import kotlin.collections.MutableMap +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.emptyList +import kotlin.collections.filterIsInstance +import kotlin.collections.isNullOrEmpty +import kotlin.collections.map +import kotlin.collections.mapNotNull +import kotlin.collections.mapOf +import kotlin.collections.mapValuesTo +import kotlin.collections.mutableMapOf +import kotlin.collections.plusAssign +import kotlin.collections.set import kotlin.io.path.absolute import kotlin.io.path.exists @@ -121,60 +136,81 @@ class CustomPlatformStep( placeholder: Placeholder, taskParentComponent: JComponent? ) { - val task = object : Task.WithResult( + properties.clear() + descriptor = null + + val baseData = data.getUserData(NewProjectWizardBaseData.KEY) + ?: return thisLogger().error("Could not find wizard base data") +// val buildSystemProps = findStep>() +// +// properties["GROUP_ID"] = CreatorProperty(buildSystemProps.groupIdProperty, StringPropertyType()) +// properties["ARTIFACT_ID"] = CreatorProperty(buildSystemProps.artifactIdProperty, StringPropertyType()) +// properties["VERSION"] = CreatorProperty(buildSystemProps.versionProperty, StringPropertyType()) + + properties["PROJECT_NAME"] = CreatorProperty(baseData.nameProperty, StringPropertyType()) + + // TODO remove + properties["GROUP_ID"] = CreatorProperty(propertyGraph.property("io.github.rednesto"), StringPropertyType()) + + val task = object : Task.WithResult>, Exception>( context.project, taskParentComponent, MCDevBundle("creator.step.generic.project_created.message"), false ) { - override fun compute(indicator: ProgressIndicator): DialogPanel? { + override fun compute(indicator: ProgressIndicator): List> { if (project?.isDisposed == true) { - return null + return emptyList() } - return doCreateOptionsPanel(path) + return setupTemplate(path) } } - placeholder.component = ProgressManager.getInstance().run(task) + placeholder.component = panel { + for (uiFactory in ProgressManager.getInstance().run(task)) { + uiFactory.accept(this) + } + } } - private fun doCreateOptionsPanel(path: String): DialogPanel? { - properties.clear() - descriptor = null + private fun setupTemplate(path: String): List> { val templateDescriptorPath = Path.of(path, ".mcdev.template.json") if (!templateDescriptorPath.exists()) { - return null + return emptyList() } - return panel { - val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) - descriptor = templateDescriptor - for (prop in templateDescriptor.properties) { - makeField(prop) - } - } + val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) + descriptor = templateDescriptor + return templateDescriptor.properties.mapNotNull { makeField(it) } } - private fun Panel.makeField(prop: TemplateProperty) { + private fun makeField(prop: TemplateProperty): Consumer? { if (prop.name in properties.keys) { - return thisLogger().error("Duplicate property name ${prop.name}") + thisLogger().error("Duplicate property name ${prop.name}") + return null } @Suppress("UNCHECKED_CAST") val type = when (prop.type) { "string" -> StringPropertyType() + "integer" -> IntegerPropertyType() "boolean" -> BooleanPropertyType() "class_fqn" -> ClassFqnPropertyType() "semantic_version" -> SemanticVersionPropertyType() - else -> return thisLogger().error("Unknown template property type ${prop.type}") + "jdk" -> JdkPropertyType() + else -> { + thisLogger().error("Unknown template property type ${prop.type}") + return null + } } as PropertyType val isDropdown = !prop.options.isNullOrEmpty() val options = prop.options?.filterIsInstance()?.map(type::deserialize) ?: emptyList() val defaultOptionIndex = if (isDropdown) prop.default as? Int ?: 0 else null - val defaultValue = type.createDefaultValue(if (isDropdown) prop.options!![defaultOptionIndex!!] else prop.default) + val defaultValue = + type.createDefaultValue(if (isDropdown) prop.options!![defaultOptionIndex!!] else prop.default) val graphProp = propertyGraph.property(defaultValue) if (prop.remember != false && prop.derives == null) { @@ -182,27 +218,54 @@ class CustomPlatformStep( } if (prop.derives != null) { - val parentProperty = properties[prop.derives.from] - ?: return thisLogger().error("Unknown parent property '${prop.derives.from}'") + val parents = prop.derives.parents + ?: run { + thisLogger().error("No parents specified in derivation of property '${prop.name}'") + return null + } + for (parent in parents) { + if (!properties.containsKey(parent)) { + thisLogger().error("Unknown parent property '${parent}' in derivation of property '${prop.name}'") + return null + } + } + + fun collectParentValues(): List = parents.map { properties[it]!!.graphProperty.get() } - graphProp.set(type.derive(graphProp, parentProperty, properties, prop.derives)) - graphProp.dependsOn(parentProperty.graphProperty, prop.derives.whenModified != false) { - type.derive(graphProp, parentProperty, properties, prop.derives) + graphProp.set(type.derive(graphProp, collectParentValues(), properties, prop.derives)) + for (parent in parents) { + val parentProperty = properties[parent]!! + graphProp.dependsOn(parentProperty.graphProperty, prop.derives.whenModified != false) { + type.derive(graphProp, collectParentValues(), properties, prop.derives) + } } } - if (isDropdown) { - row(prop.label) { - comboBox(options) - .bindItem(graphProp) - .enabled(prop.editable != false) - .also { ComboboxSpeedSearch.installOn(it.component) } - }.visible(prop.hidden != false) - } else { - with(type) { buildUi(graphProp, prop) } + if (prop.inheritFrom != null) { + val parentProperty = properties[prop.inheritFrom] + ?: run { + thisLogger().error("Unknown parent property '${prop.inheritFrom}' in derivation of property '${prop.name}'") + return null + } + + graphProp.set(parentProperty.graphProperty.get()) + graphProp.dependsOn(parentProperty.graphProperty, true) { parentProperty.graphProperty.get() } } properties[prop.name] = CreatorProperty(graphProp, type) + + return Consumer { panel -> + if (isDropdown) { + panel.row(prop.label) { + comboBox(options) + .bindItem(graphProp) + .enabled(prop.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.visible(prop.hidden != false) + } else { + with(panel) { with(type) { buildUi(context, graphProp, prop) } } + } + } } private fun makeStorageKey(prop: TemplateProperty) = @@ -213,18 +276,6 @@ class CustomPlatformStep( val rootPath = Path.of(path).absolute() - val baseData = data.getUserData(NewProjectWizardBaseData.KEY) ?: return - val javaVersion = findStep().preferredJdk.ordinal - val buildSystemProps = findStep>() - - assets.addTemplateProperties( - "PROJECT_NAME" to baseData.name, - "JAVA_VERSION" to javaVersion, - "GROUP_ID" to buildSystemProps.groupId, - "ARTIFACT_ID" to buildSystemProps.artifactId, - "VERSION" to buildSystemProps.version, - ) - collectTemplateProperties(assets.templateProperties) thisLogger().debug("Template properties: ${assets.templateProperties}") @@ -260,8 +311,22 @@ class CustomPlatformStep( } } - private fun collectTemplateProperties(into: MutableMap = mutableMapOf()) = - properties.mapValuesTo(into) { (_, prop) -> prop.graphProperty.get() } + private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { + val baseData = data.getUserData(NewProjectWizardBaseData.KEY) + ?: return into.also { thisLogger().error("Could not find wizard base data") } + val javaVersion = findStep().preferredJdk.ordinal + val buildSystemProps = findStep>() + + into += mapOf( + "PROJECT_NAME" to baseData.name, + "JAVA_VERSION" to javaVersion, + "GROUP_ID" to buildSystemProps.groupId, + "ARTIFACT_ID" to buildSystemProps.artifactId, + "VERSION" to buildSystemProps.version, + ) + + return properties.mapValuesTo(into) { (_, prop) -> prop.graphProperty.get() } + } class TypeFactory : PlatformTypeStep.Factory { override val name From 5386102e9541d00ac8f096fd8ae36ff25c3ada12 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 10 May 2024 20:04:02 +0200 Subject: [PATCH 007/118] Some more refactoring to get things working nicely --- .../buildsystem/BuildSystemPropertiesStep.kt | 2 +- .../creator/custom/TemplateDescriptor.kt | 4 +- .../custom/model/BuildSystemCoordinates.kt | 6 + .../custom/types/BooleanCreatorProperty.kt | 28 ++++ .../custom/types/BooleanPropertyType.kt | 28 ---- .../BuildSystemCoordinatesCreatorProperty.kt | 96 +++++++++++++ .../custom/types/ClassFqnCreatorProperty.kt | 50 +++++++ .../custom/types/ClassFqnPropertyType.kt | 49 ------- .../creator/custom/types/CreatorProperty.kt | 103 ++++++++++++++ .../custom/types/ExternalCreatorProperty.kt | 25 ++++ .../custom/types/IntegerCreatorProperty.kt | 48 +++++++ .../custom/types/IntegerPropertyType.kt | 27 ---- .../custom/types/JdkCreatorProperty.kt | 46 +++++++ .../creator/custom/types/JdkPropertyType.kt | 32 ----- .../creator/custom/types/PropertyType.kt | 45 ------ ...e.kt => SemanticVersionCreatorProperty.kt} | 32 ++--- .../custom/types/SimpleCreatorProperty.kt | 38 +++++ .../custom/types/StringCreatorProperty.kt | 33 +++++ .../custom/types/StringPropertyType.kt | 30 ---- .../creator/platformtype/CreatorProperty.kt | 6 - .../platformtype/CustomPlatformStep.kt | 130 +++++------------- 21 files changed, 522 insertions(+), 336 deletions(-) create mode 100644 src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt create mode 100644 src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt delete mode 100644 src/main/kotlin/creator/custom/types/BooleanPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt delete mode 100644 src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/CreatorProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt delete mode 100644 src/main/kotlin/creator/custom/types/IntegerPropertyType.kt create mode 100644 src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt delete mode 100644 src/main/kotlin/creator/custom/types/JdkPropertyType.kt delete mode 100644 src/main/kotlin/creator/custom/types/PropertyType.kt rename src/main/kotlin/creator/custom/types/{SemanticVersionPropertyType.kt => SemanticVersionCreatorProperty.kt} (66%) create mode 100644 src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/StringCreatorProperty.kt delete mode 100644 src/main/kotlin/creator/custom/types/StringPropertyType.kt delete mode 100644 src/main/kotlin/creator/platformtype/CreatorProperty.kt diff --git a/src/main/kotlin/creator/buildsystem/BuildSystemPropertiesStep.kt b/src/main/kotlin/creator/buildsystem/BuildSystemPropertiesStep.kt index bc6324f54..67cc8a3ef 100644 --- a/src/main/kotlin/creator/buildsystem/BuildSystemPropertiesStep.kt +++ b/src/main/kotlin/creator/buildsystem/BuildSystemPropertiesStep.kt @@ -52,7 +52,7 @@ class BuildSystemPropertiesStep(private val parent: ParentStep) : Ab val groupIdProperty = propertyGraph.property("org.example") .bindStorage("${javaClass.name}.groupId") val artifactIdProperty = propertyGraph.lazyProperty(::suggestArtifactId) - private val versionProperty = propertyGraph.property("1.0-SNAPSHOT") + val versionProperty = propertyGraph.property("1.0-SNAPSHOT") .bindStorage("${javaClass.name}.version") var groupId by groupIdProperty diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index e9a8af5ac..a4ee6b4d6 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -1,11 +1,11 @@ package com.demonwav.mcdev.creator.custom data class TemplateDescriptor( - val properties: List, + val properties: List, val files: List, ) -data class TemplateProperty( +data class TemplatePropertyDescriptor( val name: String, val type: String, val label: String, diff --git a/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt new file mode 100644 index 000000000..0a1f182a6 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt @@ -0,0 +1,6 @@ +package com.demonwav.mcdev.creator.custom.model + +data class BuildSystemCoordinates(val groupId: String, val artifactId: String, val version: String) { + + override fun toString(): String = "$groupId:$artifactId:$version" +} diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt new file mode 100644 index 000000000..02dec425c --- /dev/null +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -0,0 +1,28 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindSelected + +class BooleanCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + override fun createDefaultValue(raw: Any?): Boolean = raw as? Boolean ?: false + + override fun serialize(value: Boolean): String = value.toString() + + override fun deserialize(string: String): Boolean = string.toBoolean() + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.checkBox(descriptor.label) + .bindSelected(graphProperty) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) + } +} diff --git a/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt b/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt deleted file mode 100644 index d528cd956..000000000 --- a/src/main/kotlin/creator/custom/types/BooleanPropertyType.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types - -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.ui.dsl.builder.Panel -import com.intellij.ui.dsl.builder.bindSelected - -class BooleanPropertyType : PropertyType { - - override fun createDefaultValue(raw: Any?): Boolean = raw as? Boolean ?: false - - override fun serialize(value: Boolean): String = value.toString() - - override fun deserialize(string: String): Boolean = string.toBoolean() - - override fun Panel.buildUi( - context: WizardContext, - graphProperty: GraphProperty, - property: TemplateProperty - ) { - row(property.label) { - checkBox(property.label) - .bindSelected(graphProperty) - .enabled(property.editable != false) - }.visible(property.hidden != false) - } -} diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt new file mode 100644 index 000000000..d4f0bc66b --- /dev/null +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -0,0 +1,96 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.dsl.builder.COLUMNS_MEDIUM +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class BuildSystemCoordinatesCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val default = createDefaultValue(descriptor.default) + + override val graphProperty: GraphProperty = graph.property(default) + var coords: BuildSystemCoordinates by graphProperty + + private val groupIdProperty = graphProperty.transform({ it.groupId }, { coords.copy(groupId = it) }) + private val artifactIdProperty = graphProperty.transform({ it.artifactId }, { coords.copy(artifactId = it) }) + private val versionProperty = graphProperty.transform({ it.version }, { coords.copy(version = it) }) + + override fun createDefaultValue(raw: Any?): BuildSystemCoordinates { + val str = (raw as? String) ?: return createDefaultValue() + return deserialize(str) + } + + private fun createDefaultValue() = BuildSystemCoordinates("", "", "") + + override fun serialize(value: BuildSystemCoordinates): String = + "${value.groupId}:${value.artifactId}:${value.version}" + + override fun deserialize(string: String): BuildSystemCoordinates { + val segments = string.split(':') + + val groupId = segments.getOrElse(0) { "" } + val artifactId = segments.getOrElse(1) { "" } + val version = segments.getOrElse(2) { "" } + return BuildSystemCoordinates(groupId, artifactId, version) + } + + override fun setupProperty() { + super.setupProperty() + + val projectNameProperty = properties["PROJECT_NAME"]?.graphProperty + if (projectNameProperty != null) { + val projectName = projectNameProperty.get() + if (projectName is String) { + coords = coords.copy(artifactId = projectName) + } + + graphProperty.dependsOn(projectNameProperty, false) { + val newProjectName = projectNameProperty.get() + if (newProjectName is String) { + coords.copy(artifactId = newProjectName) + } else { + coords + } + } + } + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.collapsibleGroup(MCDevBundle("creator.ui.group.title")) { + this.row(MCDevBundle("creator.ui.group.group_id")) { + this.textField() + .bindText(this@BuildSystemCoordinatesCreatorProperty.groupIdProperty) + .columns(COLUMNS_MEDIUM) +// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) +// .textValidation(CHECK_NON_EMPTY, CHECK_GROUP_ID, nonExampleValidation) + } + this.row(MCDevBundle("creator.ui.group.artifact_id")) { + this.textField() + .bindText(this@BuildSystemCoordinatesCreatorProperty.artifactIdProperty) + .columns(COLUMNS_MEDIUM) +// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) +// .textValidation(CHECK_NON_EMPTY, CHECK_ARTIFACT_ID) + } + this.row(MCDevBundle("creator.ui.group.version")) { + this.textField() + .bindText(this@BuildSystemCoordinatesCreatorProperty.versionProperty) + .columns(COLUMNS_MEDIUM) +// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) +// .textValidation(versionValidation) + } + }.expanded = true + + } +} diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt new file mode 100644 index 000000000..680a59284 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -0,0 +1,50 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates +import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.demonwav.mcdev.util.capitalize +import com.demonwav.mcdev.util.decapitalize +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class ClassFqnCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + override fun createDefaultValue(raw: Any?): ClassFqn = ClassFqn(raw as? String ?: "") + + override fun serialize(value: ClassFqn): String = value.toString() + + override fun deserialize(string: String): ClassFqn = ClassFqn(string) + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.textField().bindText(this@ClassFqnCreatorProperty.toStringProperty(graphProperty)) + .columns(COLUMNS_LARGE) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) + } + + override fun derive(parentValues: List, derivation: PropertyDerivation): ClassFqn { + return when (derivation.method) { + "suggestClassName" -> suggestClassName(parentValues) + else -> throw IllegalArgumentException("Unknown method derivation $derivation") + } + } + + private fun suggestClassName(parentValues: List): ClassFqn { + val coords = parentValues.getOrNull(0) as? BuildSystemCoordinates + ?: throw RuntimeException("Expected parent 0 to be a build system coordinates") + val name = parentValues.getOrNull(1) as? String + ?: throw RuntimeException("Expected parent 1 to be a string") + return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}") + } +} diff --git a/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt b/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt deleted file mode 100644 index f4b013888..000000000 --- a/src/main/kotlin/creator/custom/types/ClassFqnPropertyType.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types - -import com.demonwav.mcdev.creator.custom.PropertyDerivation -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.custom.model.ClassFqn -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.ui.dsl.builder.COLUMNS_LARGE -import com.intellij.ui.dsl.builder.Panel -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.columns - -class ClassFqnPropertyType : PropertyType { - - override fun createDefaultValue(raw: Any?): ClassFqn = ClassFqn(raw as? String ?: "") - - override fun serialize(value: ClassFqn): String = value.toString() - - override fun deserialize(string: String): ClassFqn = ClassFqn(string) - - override fun Panel.buildUi( - context: WizardContext, - graphProperty: GraphProperty, - property: TemplateProperty - ) { - row(property.label) { - textField().bindText(toStringProperty(graphProperty)) - .columns(COLUMNS_LARGE) - .enabled(property.editable != false) - }.visible(property.hidden != false) - } - - override fun derive( - property: GraphProperty, - parentValues: List, - properties: Map, - derivation: PropertyDerivation - ): ClassFqn { - return when (derivation.method) { - "suggestClassName" -> suggestClassName(parentValues) - else -> throw IllegalArgumentException("Unknown method derivation $derivation") - } - } - - private fun suggestClassName(parentValues: List): ClassFqn { - val (groupId, name) = parentValues - return ClassFqn("$groupId.$name.$name") - } -} diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt new file mode 100644 index 000000000..4aca89d6f --- /dev/null +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -0,0 +1,103 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.ObservableMutableProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.bindStorage +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem + +abstract class CreatorProperty( + val descriptor: TemplatePropertyDescriptor, + val graph: PropertyGraph, + protected val properties: Map> +) { + abstract val graphProperty: GraphProperty + + abstract fun createDefaultValue(raw: Any?): T + + abstract fun serialize(value: T): String + + abstract fun deserialize(string: String): T + + open fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = + graphProperty.transform(::serialize, ::deserialize) + + /** + * Produces a new value based on the provided [parentValues] and the template-defined [derivation] configuration. + * + * You must **NOT** [set][GraphProperty.set] the value of [graphProperty] in the process. You may however [get][GraphProperty.get] it at will. + * + * @param parentValues the values of the properties this [graphProperty] depends on + * @param derivation the configuration of the desired derivation + * + * @see GraphProperty.dependsOn + */ + open fun derive(parentValues: List, derivation: PropertyDerivation): Any? { + thisLogger().error("This type doesn't support derivation") + return graphProperty.get() + } + + abstract fun buildUi(panel: Panel, context: WizardContext) + + open fun setupProperty() { + if (descriptor.remember != false && descriptor.derives == null) { + toStringProperty(graphProperty).bindStorage(makeStorageKey()) + } + + if (descriptor.derives != null) { + val parents = descriptor.derives.parents + ?: throw RuntimeException("No parents specified in derivation of property '${descriptor.name}'") + for (parent in parents) { + if (!properties.containsKey(parent)) { + throw RuntimeException("Unknown parent property '${parent}' in derivation of property '${descriptor.name}'") + } + } + + fun collectParentValues(): List = parents.map { properties[it]!!.graphProperty.get() } + + @Suppress("UNCHECKED_CAST") + graphProperty.set(derive(collectParentValues(), descriptor.derives) as T) + for (parent in parents) { + val parentProperty = properties[parent]!! + graphProperty.dependsOn(parentProperty.graphProperty, descriptor.derives.whenModified != false) { + @Suppress("UNCHECKED_CAST") + derive(collectParentValues(), descriptor.derives) as T + } + } + } + + if (descriptor.inheritFrom != null) { + val parentProperty = properties[descriptor.inheritFrom] + ?: throw RuntimeException("Unknown parent property '${descriptor.inheritFrom}' in derivation of property '${descriptor.name}'") + + @Suppress("UNCHECKED_CAST") + graphProperty.set(parentProperty.graphProperty.get() as T) + graphProperty.dependsOn(parentProperty.graphProperty, true) { parentProperty.graphProperty.get() as T } + } + } + + private fun makeStorageKey(discriminator: String? = null): String { + val base = "${javaClass.name}.property.${descriptor.name}.${descriptor.type}" + if (discriminator == null) { + return base + } + + return "$base.$discriminator" + } + + protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { + row(descriptor.label) { + comboBox(options) + .bindItem(graphProp) + .enabled(descriptor.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.visible(descriptor.hidden != true) + } +} diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt new file mode 100644 index 000000000..ad56285ce --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -0,0 +1,25 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.Panel + +class ExternalCreatorProperty( + graph: PropertyGraph, + properties: Map>, + override val graphProperty: GraphProperty, + descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", null, null, null, null, "", null, null), +) : CreatorProperty(descriptor, graph, properties) { + + override fun setupProperty() = Unit + + override fun createDefaultValue(raw: Any?): T = throw UnsupportedOperationException("Unsupported for external properties") + + override fun serialize(value: T): String = throw UnsupportedOperationException("Unsupported for external properties") + + override fun deserialize(string: String): T = throw UnsupportedOperationException("Unsupported for external properties") + + override fun buildUi(panel: Panel, context: WizardContext) = Unit +} diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt new file mode 100644 index 000000000..8b3e8383d --- /dev/null +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -0,0 +1,48 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.util.MinecraftVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindIntText +import com.intellij.ui.dsl.builder.columns + +class IntegerCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 + + override fun serialize(value: Int): String = value.toString() + + override fun deserialize(string: String): Int = string.toIntOrNull() ?: 0 + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.intTextField().bindIntText(graphProperty) + .columns(COLUMNS_LARGE) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) + } + + override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { + return when (derivation.method) { + "recommendJavaVersionForMcVersion" -> recommendJavaVersionForMcVersion(parentValues[0]) + else -> throw IllegalArgumentException("Unknown method derivation $derivation") + } + } + + private fun recommendJavaVersionForMcVersion(from: Any?): Int { + if (from !is SemanticVersion) { + return 17 + } + + return MinecraftVersions.requiredJavaVersion(from).ordinal + } +} diff --git a/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt b/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt deleted file mode 100644 index afe2b4cbd..000000000 --- a/src/main/kotlin/creator/custom/types/IntegerPropertyType.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types.creator.custom.types - -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.custom.types.PropertyType -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.ui.dsl.builder.COLUMNS_LARGE -import com.intellij.ui.dsl.builder.Panel -import com.intellij.ui.dsl.builder.bindIntText -import com.intellij.ui.dsl.builder.columns - -class IntegerPropertyType : PropertyType { - - override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 - - override fun serialize(value: Int): String = value.toString() - - override fun deserialize(string: String): Int = string.toIntOrNull() ?: 0 - - override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { - row(property.label) { - intTextField().bindIntText(graphProperty) - .columns(COLUMNS_LARGE) - .enabled(property.editable != false) - }.visible(property.hidden != false) - } -} diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt new file mode 100644 index 000000000..5816d06e0 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -0,0 +1,46 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.JdkComboBoxWithPreference +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.CreatorJdk +import com.demonwav.mcdev.creator.jdkComboBoxWithPreference +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.openapi.projectRoots.JavaSdkVersion +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.ui.dsl.builder.Panel + +class JdkCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + private lateinit var jdkComboBox: JdkComboBoxWithPreference + + override fun createDefaultValue(raw: Any?): CreatorJdk = CreatorJdk(null) + + override fun serialize(value: CreatorJdk): String = value.sdk?.homePath ?: "" + + override fun deserialize(string: String): CreatorJdk = + CreatorJdk(ProjectJdkTable.getInstance().allJdks.find { it.homePath == string }) + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + val sdkProperty = graphProperty.transform(CreatorJdk::sdk, ::CreatorJdk) + jdkComboBox = this.jdkComboBoxWithPreference(context, sdkProperty, descriptor.name).component + + val minVersionPropName = descriptor.default as? String + if (minVersionPropName != null) { + val minVersionProperty = properties[minVersionPropName] + ?: throw RuntimeException("Could not find property $minVersionPropName referenced by default value of property ${descriptor.name}") + + jdkComboBox.setPreferredJdk(JavaSdkVersion.entries[minVersionProperty.graphProperty.get() as Int]) + minVersionProperty.graphProperty.afterPropagation { + jdkComboBox.setPreferredJdk(JavaSdkVersion.entries[minVersionProperty.graphProperty.get() as Int]) + } + } + } + } +} diff --git a/src/main/kotlin/creator/custom/types/JdkPropertyType.kt b/src/main/kotlin/creator/custom/types/JdkPropertyType.kt deleted file mode 100644 index 81735860f..000000000 --- a/src/main/kotlin/creator/custom/types/JdkPropertyType.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types - -import com.demonwav.mcdev.creator.JdkComboBoxWithPreference -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.custom.model.CreatorJdk -import com.demonwav.mcdev.creator.jdkComboBoxWithPreference -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.util.transform -import com.intellij.openapi.projectRoots.JavaSdkVersion -import com.intellij.openapi.projectRoots.ProjectJdkTable -import com.intellij.ui.dsl.builder.Panel - -class JdkPropertyType : PropertyType { - - override fun createDefaultValue(raw: Any?): CreatorJdk = CreatorJdk(null) - - override fun serialize(value: CreatorJdk): String = value.sdk?.homePath ?: "" - - override fun deserialize(string: String): CreatorJdk = - CreatorJdk(ProjectJdkTable.getInstance().allJdks.find { it.homePath == string }) - - override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { - val preferredVersion = property.default as? Int ?: 17 - lateinit var jdkComboBox: JdkComboBoxWithPreference - row(property.label) { - val sdkProperty = graphProperty.transform(CreatorJdk::sdk, ::CreatorJdk) - jdkComboBox = jdkComboBoxWithPreference(context, sdkProperty, property.name).component - jdkComboBox.setPreferredJdk(JavaSdkVersion.entries[preferredVersion]) - } - } -} diff --git a/src/main/kotlin/creator/custom/types/PropertyType.kt b/src/main/kotlin/creator/custom/types/PropertyType.kt deleted file mode 100644 index 5ad1142e7..000000000 --- a/src/main/kotlin/creator/custom/types/PropertyType.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types - -import com.demonwav.mcdev.creator.custom.PropertyDerivation -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.ObservableMutableProperty -import com.intellij.openapi.observable.util.transform -import com.intellij.ui.dsl.builder.Panel - -interface PropertyType { - - fun createDefaultValue(raw: Any?): T - - fun serialize(value: T): String - - fun deserialize(string: String): T - - fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = - graphProperty.transform(::serialize, ::deserialize) - - /** - * Produces a new value based on the provided [parentValues] property value and the template-defined [derivation] configuration. - * - * You must **NOT** [set][GraphProperty.set] the value of [property] in the process. You may however [get][GraphProperty.get] it at will. - * - * @param property the property depending on [parentValues] - * @param parentValues the GraphProperty and PropertyType this [property] depends on - * @param derivation the configuration of the desired derivation - * - * @see GraphProperty.dependsOn - */ - fun derive( - property: GraphProperty, - parentValues: List, - properties: Map, - derivation: PropertyDerivation - ): Any? { - thisLogger().error("This type doesn't support derivation") - return property.get() - } - - fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) -} diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt similarity index 66% rename from src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt rename to src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 34b984eb1..0aa7ce3b8 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionPropertyType.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -1,16 +1,21 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation -import com.demonwav.mcdev.creator.custom.TemplateProperty +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.COLUMNS_SHORT import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns -class SemanticVersionPropertyType : PropertyType { +class SemanticVersionCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { override fun createDefaultValue(raw: Any?): SemanticVersion = SemanticVersion.tryParse(raw as? String ?: "") ?: SemanticVersion(emptyList()) @@ -20,24 +25,15 @@ class SemanticVersionPropertyType : PropertyType { override fun deserialize(string: String): SemanticVersion = SemanticVersion.tryParse(string) ?: SemanticVersion(emptyList()) - override fun Panel.buildUi( - context: WizardContext, - graphProperty: GraphProperty, - property: TemplateProperty - ) { - row(property.label) { - textField().bindText(toStringProperty(graphProperty)) + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.textField().bindText(this@SemanticVersionCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_SHORT) - .enabled(property.editable != false) - }.visible(property.hidden != false) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) } - override fun derive( - property: GraphProperty, - parentValues: List, - properties: Map, - derivation: PropertyDerivation - ): SemanticVersion { + override fun derive(parentValues: List, derivation: PropertyDerivation): Any { return when (derivation.method) { "extractVersionMajorMinor" -> extractVersionMajorMinor(parentValues[0]) else -> throw IllegalArgumentException("Unknown method derivation $derivation") diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt new file mode 100644 index 000000000..49373f77a --- /dev/null +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -0,0 +1,38 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem + +abstract class SimpleCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val isDropdown = !descriptor.options.isNullOrEmpty() + private val options = descriptor.options?.filterIsInstance()?.map(::deserialize) ?: emptyList() + private val defaultOptionIndex = if (isDropdown) descriptor.default as? Int ?: 0 else null + private val defaultValue by lazy { createDefaultValue(if (isDropdown) descriptor.options!![defaultOptionIndex!!] else descriptor.default) } + + override val graphProperty: GraphProperty by lazy { graph.property(defaultValue) } + + override fun buildUi(panel: Panel, context: WizardContext) { + if (isDropdown) { + panel.row(descriptor.label) { + comboBox(options) + .bindItem(graphProperty) + .enabled(descriptor.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.visible(descriptor.hidden != true) + } else { + buildSimpleUi(panel, context) + } + } + + abstract fun buildSimpleUi(panel: Panel, context: WizardContext) +} diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt new file mode 100644 index 000000000..85ead8707 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -0,0 +1,33 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class StringCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" + + override fun serialize(value: String): String = value + + override fun deserialize(string: String): String = string + + override fun toStringProperty(graphProperty: GraphProperty) = graphProperty + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) + .columns(COLUMNS_LARGE) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) + } +} diff --git a/src/main/kotlin/creator/custom/types/StringPropertyType.kt b/src/main/kotlin/creator/custom/types/StringPropertyType.kt deleted file mode 100644 index d11955985..000000000 --- a/src/main/kotlin/creator/custom/types/StringPropertyType.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.demonwav.mcdev.creator.custom.types - -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.ObservableMutableProperty -import com.intellij.ui.dsl.builder.COLUMNS_LARGE -import com.intellij.ui.dsl.builder.Panel -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.columns - -class StringPropertyType : PropertyType { - - override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" - - override fun serialize(value: String): String = value - - override fun deserialize(string: String): String = string - - override fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = - graphProperty - - override fun Panel.buildUi(context: WizardContext, graphProperty: GraphProperty, property: TemplateProperty) { - row(property.label) { - textField().bindText(toStringProperty(graphProperty)) - .columns(COLUMNS_LARGE) - .enabled(property.editable != false) - }.visible(property.hidden != false) - } -} diff --git a/src/main/kotlin/creator/platformtype/CreatorProperty.kt b/src/main/kotlin/creator/platformtype/CreatorProperty.kt deleted file mode 100644 index b048903e0..000000000 --- a/src/main/kotlin/creator/platformtype/CreatorProperty.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.demonwav.mcdev.creator.platformtype - -import com.demonwav.mcdev.creator.custom.types.PropertyType -import com.intellij.openapi.observable.properties.ObservableProperty - -data class CreatorProperty(val graphProperty: ObservableProperty, val type: PropertyType) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 4defbe0ca..8b19e6a4a 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -25,14 +25,16 @@ import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator -import com.demonwav.mcdev.creator.custom.TemplateProperty -import com.demonwav.mcdev.creator.custom.types.BooleanPropertyType -import com.demonwav.mcdev.creator.custom.types.ClassFqnPropertyType -import com.demonwav.mcdev.creator.custom.types.JdkPropertyType -import com.demonwav.mcdev.creator.custom.types.PropertyType -import com.demonwav.mcdev.creator.custom.types.SemanticVersionPropertyType -import com.demonwav.mcdev.creator.custom.types.StringPropertyType -import com.demonwav.mcdev.creator.custom.types.creator.custom.types.IntegerPropertyType +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.types.BooleanCreatorProperty +import com.demonwav.mcdev.creator.custom.types.BuildSystemCoordinatesCreatorProperty +import com.demonwav.mcdev.creator.custom.types.ClassFqnCreatorProperty +import com.demonwav.mcdev.creator.custom.types.JdkCreatorProperty +import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty +import com.demonwav.mcdev.creator.custom.types.SemanticVersionCreatorProperty +import com.demonwav.mcdev.creator.custom.types.StringCreatorProperty +import com.demonwav.mcdev.creator.custom.types.IntegerCreatorProperty import com.demonwav.mcdev.creator.findStep import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep import com.demonwav.mcdev.util.fromJson @@ -42,6 +44,7 @@ import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager @@ -50,12 +53,10 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder -import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel @@ -69,9 +70,6 @@ import kotlin.collections.MutableMap import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.emptyList -import kotlin.collections.filterIsInstance -import kotlin.collections.isNullOrEmpty -import kotlin.collections.map import kotlin.collections.mapNotNull import kotlin.collections.mapOf import kotlin.collections.mapValuesTo @@ -98,7 +96,7 @@ class CustomPlatformStep( val descriptorProperty = propertyGraph.property(null) var descriptor by descriptorProperty - private val properties = mutableMapOf>() + private var properties = mutableMapOf>() override fun setupUI(builder: Panel) { var taskParentComponent: JComponent? = null @@ -136,21 +134,13 @@ class CustomPlatformStep( placeholder: Placeholder, taskParentComponent: JComponent? ) { - properties.clear() + properties = mutableMapOf() descriptor = null val baseData = data.getUserData(NewProjectWizardBaseData.KEY) ?: return thisLogger().error("Could not find wizard base data") -// val buildSystemProps = findStep>() -// -// properties["GROUP_ID"] = CreatorProperty(buildSystemProps.groupIdProperty, StringPropertyType()) -// properties["ARTIFACT_ID"] = CreatorProperty(buildSystemProps.artifactIdProperty, StringPropertyType()) -// properties["VERSION"] = CreatorProperty(buildSystemProps.versionProperty, StringPropertyType()) - properties["PROJECT_NAME"] = CreatorProperty(baseData.nameProperty, StringPropertyType()) - - // TODO remove - properties["GROUP_ID"] = CreatorProperty(propertyGraph.property("io.github.rednesto"), StringPropertyType()) + properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) val task = object : Task.WithResult>, Exception>( context.project, @@ -183,94 +173,38 @@ class CustomPlatformStep( val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) descriptor = templateDescriptor - return templateDescriptor.properties.mapNotNull { makeField(it) } + return templateDescriptor.properties.mapNotNull { setupProperty(it) } } - private fun makeField(prop: TemplateProperty): Consumer? { - if (prop.name in properties.keys) { - thisLogger().error("Duplicate property name ${prop.name}") + private fun setupProperty(descriptor: TemplatePropertyDescriptor): Consumer? { + if (descriptor.name in properties.keys) { + thisLogger().error("Duplicate property name ${descriptor.name}") return null } - @Suppress("UNCHECKED_CAST") - val type = when (prop.type) { - "string" -> StringPropertyType() - "integer" -> IntegerPropertyType() - "boolean" -> BooleanPropertyType() - "class_fqn" -> ClassFqnPropertyType() - "semantic_version" -> SemanticVersionPropertyType() - "jdk" -> JdkPropertyType() + // TODO make this an EP + val propFactory: (PropertyGraph, TemplatePropertyDescriptor, Map>) -> CreatorProperty<*> = when (descriptor.type) { + "string" -> ::StringCreatorProperty + "integer" -> ::IntegerCreatorProperty + "boolean" -> ::BooleanCreatorProperty + "class_fqn" -> ::ClassFqnCreatorProperty + "semantic_version" -> ::SemanticVersionCreatorProperty + "jdk" -> ::JdkCreatorProperty + "build_system_coordinates" -> ::BuildSystemCoordinatesCreatorProperty else -> { - thisLogger().error("Unknown template property type ${prop.type}") + thisLogger().error("Unknown template property type ${descriptor.type}") return null } - } as PropertyType - - val isDropdown = !prop.options.isNullOrEmpty() - val options = prop.options?.filterIsInstance()?.map(type::deserialize) ?: emptyList() - val defaultOptionIndex = if (isDropdown) prop.default as? Int ?: 0 else null - val defaultValue = - type.createDefaultValue(if (isDropdown) prop.options!![defaultOptionIndex!!] else prop.default) - val graphProp = propertyGraph.property(defaultValue) - - if (prop.remember != false && prop.derives == null) { - type.toStringProperty(graphProp).bindStorage(makeStorageKey(prop)) } - if (prop.derives != null) { - val parents = prop.derives.parents - ?: run { - thisLogger().error("No parents specified in derivation of property '${prop.name}'") - return null - } - for (parent in parents) { - if (!properties.containsKey(parent)) { - thisLogger().error("Unknown parent property '${parent}' in derivation of property '${prop.name}'") - return null - } - } + val prop = propFactory(propertyGraph, descriptor, properties) + prop.setupProperty() - fun collectParentValues(): List = parents.map { properties[it]!!.graphProperty.get() } + properties[descriptor.name] = prop - graphProp.set(type.derive(graphProp, collectParentValues(), properties, prop.derives)) - for (parent in parents) { - val parentProperty = properties[parent]!! - graphProp.dependsOn(parentProperty.graphProperty, prop.derives.whenModified != false) { - type.derive(graphProp, collectParentValues(), properties, prop.derives) - } - } - } - - if (prop.inheritFrom != null) { - val parentProperty = properties[prop.inheritFrom] - ?: run { - thisLogger().error("Unknown parent property '${prop.inheritFrom}' in derivation of property '${prop.name}'") - return null - } - - graphProp.set(parentProperty.graphProperty.get()) - graphProp.dependsOn(parentProperty.graphProperty, true) { parentProperty.graphProperty.get() } - } - - properties[prop.name] = CreatorProperty(graphProp, type) - - return Consumer { panel -> - if (isDropdown) { - panel.row(prop.label) { - comboBox(options) - .bindItem(graphProp) - .enabled(prop.editable != false) - .also { ComboboxSpeedSearch.installOn(it.component) } - }.visible(prop.hidden != false) - } else { - with(panel) { with(type) { buildUi(context, graphProp, prop) } } - } - } + return Consumer { panel -> prop.buildUi(panel, context) } } - private fun makeStorageKey(prop: TemplateProperty) = - "${CustomPlatformStep::class.java.name}.property.${prop.name}.${prop.type}" - override fun setupAssets(project: Project) { val descriptor = descriptor!! From b7ab23db0e8099d2575d9d03e84ac34c08b2478f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 10 May 2024 21:49:32 +0200 Subject: [PATCH 008/118] Move CreatorProperties to an EP --- .../custom/types/BooleanCreatorProperty.kt | 8 +++ .../BuildSystemCoordinatesCreatorProperty.kt | 8 +++ .../custom/types/ClassFqnCreatorProperty.kt | 8 +++ .../custom/types/CreatorPropertyFactory.kt | 52 +++++++++++++++++++ .../custom/types/IntegerCreatorProperty.kt | 8 +++ .../custom/types/JdkCreatorProperty.kt | 8 +++ .../types/SemanticVersionCreatorProperty.kt | 8 +++ .../custom/types/StringCreatorProperty.kt | 8 +++ .../platformtype/CustomPlatformStep.kt | 19 ++----- src/main/resources/META-INF/plugin.xml | 12 +++++ 10 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index 02dec425c..c20b70068 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -25,4 +25,12 @@ class BooleanCreatorProperty( .enabled(descriptor.editable != false) }.visible(descriptor.hidden != true) } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = BooleanCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index d4f0bc66b..53273d658 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -93,4 +93,12 @@ class BuildSystemCoordinatesCreatorProperty( }.expanded = true } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = BuildSystemCoordinatesCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 680a59284..0f853890e 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -47,4 +47,12 @@ class ClassFqnCreatorProperty( ?: throw RuntimeException("Expected parent 1 to be a string") return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}") } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = ClassFqnCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt new file mode 100644 index 000000000..b47c66b80 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt @@ -0,0 +1,52 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.RequiredElement +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.util.KeyedExtensionCollector +import com.intellij.serviceContainer.BaseKeyedLazyInstance +import com.intellij.util.KeyedLazyInstance +import com.intellij.util.xmlb.annotations.Attribute + +interface CreatorPropertyFactory { + + companion object { + + private val EP_NAME = + ExtensionPointName>("com.demonwav.minecraft-dev.creatorPropertyType") + + private val COLLECTOR = KeyedExtensionCollector(EP_NAME) + + fun createFromType( + type: String, + descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, + properties: Map> + ): CreatorProperty<*>? { + return COLLECTOR.findSingle(type)?.create(graph, descriptor, properties) + } + } + + fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> +} + +class CreatorPropertyFactoryBean : BaseKeyedLazyInstance(), + KeyedLazyInstance { + + @Attribute("type") + @RequiredElement + lateinit var type: String + + @Attribute("implementation") + @RequiredElement + lateinit var implementation: String + + override fun getImplementationClassName(): String = implementation + + override fun getKey(): String = type +} diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 8b3e8383d..6a1c96bac 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -45,4 +45,12 @@ class IntegerCreatorProperty( return MinecraftVersions.requiredJavaVersion(from).ordinal } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = IntegerCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index 5816d06e0..41da4b977 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -43,4 +43,12 @@ class JdkCreatorProperty( } } } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = JdkCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 0aa7ce3b8..ee4294e1d 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -58,4 +58,12 @@ class SemanticVersionCreatorProperty( return SemanticVersion(emptyList()) } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = SemanticVersionCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 85ead8707..a883cba83 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -30,4 +30,12 @@ class StringCreatorProperty( .enabled(descriptor.editable != false) }.visible(descriptor.hidden != true) } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = StringCreatorProperty(graph, descriptor, properties) + } } diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 8b19e6a4a..0532f408e 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -31,6 +31,7 @@ import com.demonwav.mcdev.creator.custom.types.BuildSystemCoordinatesCreatorProp import com.demonwav.mcdev.creator.custom.types.ClassFqnCreatorProperty import com.demonwav.mcdev.creator.custom.types.JdkCreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty import com.demonwav.mcdev.creator.custom.types.SemanticVersionCreatorProperty import com.demonwav.mcdev.creator.custom.types.StringCreatorProperty @@ -182,22 +183,12 @@ class CustomPlatformStep( return null } - // TODO make this an EP - val propFactory: (PropertyGraph, TemplatePropertyDescriptor, Map>) -> CreatorProperty<*> = when (descriptor.type) { - "string" -> ::StringCreatorProperty - "integer" -> ::IntegerCreatorProperty - "boolean" -> ::BooleanCreatorProperty - "class_fqn" -> ::ClassFqnCreatorProperty - "semantic_version" -> ::SemanticVersionCreatorProperty - "jdk" -> ::JdkCreatorProperty - "build_system_coordinates" -> ::BuildSystemCoordinatesCreatorProperty - else -> { - thisLogger().error("Unknown template property type ${descriptor.type}") - return null - } + val prop = CreatorPropertyFactory.createFromType(descriptor.type, descriptor, propertyGraph, properties) + if (prop == null) { + thisLogger().error("Unknown template property type ${descriptor.type}") + return null } - val prop = propFactory(propertyGraph, descriptor, properties) prop.setupProperty() properties[descriptor.name] = prop diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ed9932681..f55a52d41 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -74,6 +74,10 @@ + + + + @@ -124,6 +128,14 @@ + + + + + + + + From 11256de944af49f1ab77766c8c8be6c9751b6e6e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 12 May 2024 12:13:33 +0200 Subject: [PATCH 009/118] Add property UI order --- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 1 + .../kotlin/creator/custom/types/CreatorProperty.kt | 5 ++++- .../creator/custom/types/ExternalCreatorProperty.kt | 2 +- .../kotlin/creator/platformtype/CustomPlatformStep.kt | 11 ++++++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index a4ee6b4d6..e667752ab 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -9,6 +9,7 @@ data class TemplatePropertyDescriptor( val name: String, val type: String, val label: String, + val order: Int?, val options: List?, val remember: Boolean?, val hidden: Boolean?, diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 4aca89d6f..d7d7dbb5b 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -79,7 +79,10 @@ abstract class CreatorProperty( @Suppress("UNCHECKED_CAST") graphProperty.set(parentProperty.graphProperty.get() as T) - graphProperty.dependsOn(parentProperty.graphProperty, true) { parentProperty.graphProperty.get() as T } + graphProperty.dependsOn(parentProperty.graphProperty, true) { + @Suppress("UNCHECKED_CAST") + parentProperty.graphProperty.get() as T + } } } diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index ad56285ce..573588595 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -10,7 +10,7 @@ class ExternalCreatorProperty( graph: PropertyGraph, properties: Map>, override val graphProperty: GraphProperty, - descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", null, null, null, null, "", null, null), + descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", null, null, null, null, null, "", null, null), ) : CreatorProperty(descriptor, graph, properties) { override fun setupProperty() = Unit diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 0532f408e..fbe4fa31b 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -174,10 +174,13 @@ class CustomPlatformStep( val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) descriptor = templateDescriptor - return templateDescriptor.properties.mapNotNull { setupProperty(it) } + return templateDescriptor.properties + .mapNotNull { setupProperty(it) } + .sortedBy { (_, order) -> order } + .map { it.first } } - private fun setupProperty(descriptor: TemplatePropertyDescriptor): Consumer? { + private fun setupProperty(descriptor: TemplatePropertyDescriptor): Pair, Int>? { if (descriptor.name in properties.keys) { thisLogger().error("Duplicate property name ${descriptor.name}") return null @@ -193,7 +196,9 @@ class CustomPlatformStep( properties[descriptor.name] = prop - return Consumer { panel -> prop.buildUi(panel, context) } + val factory = Consumer { panel -> prop.buildUi(panel, context) } + val order = descriptor.order ?: 0 + return factory to order } override fun setupAssets(project: Project) { From f6e3682e8ad820d129cd99e461434371f9ce7f2e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 17:47:52 +0200 Subject: [PATCH 010/118] Move custom template to a separate module builder --- .../custom/CustomMinecraftModuleBuilder.kt | 59 +++++++++++++++++++ .../platformtype/CustomPlatformStep.kt | 39 +----------- src/main/resources/META-INF/plugin.xml | 2 +- 3 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt diff --git a/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt new file mode 100644 index 000000000..13b6dd983 --- /dev/null +++ b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt @@ -0,0 +1,59 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.asset.PlatformAssets +import com.demonwav.mcdev.creator.platformtype.CustomPlatformStep +import com.demonwav.mcdev.creator.step.NewProjectWizardChainStep.Companion.nextStep +import com.intellij.ide.projectWizard.ProjectSettingsStep +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.ide.wizard.AbstractNewProjectWizardBuilder +import com.intellij.ide.wizard.GitNewProjectWizardStep +import com.intellij.ide.wizard.NewProjectWizardBaseStep +import com.intellij.ide.wizard.RootNewProjectWizardStep +import com.intellij.openapi.roots.ModifiableRootModel + +class CustomMinecraftModuleBuilder : AbstractNewProjectWizardBuilder() { + + override fun getPresentableName() = "Minecraft Custom Template" + override fun getNodeIcon() = PlatformAssets.MINECRAFT_ICON + override fun getGroupName() = "Minecraft" + override fun getBuilderId() = "CUSTOM_MINECRAFT_MODULE" + override fun getDescription() = MCDevBundle("creator.ui.create_minecraft_project") + + override fun setupRootModel(modifiableRootModel: ModifiableRootModel) { + if (moduleJdk != null) { + modifiableRootModel.sdk = moduleJdk + } else { + modifiableRootModel.inheritSdk() + } + } + + override fun getParentGroup() = "Minecraft" + + override fun createStep(context: WizardContext) = RootNewProjectWizardStep(context) + .nextStep(::NewProjectWizardBaseStep) + .nextStep(::GitNewProjectWizardStep) + .nextStep(::CustomPlatformStep) + + override fun getIgnoredSteps() = listOf(ProjectSettingsStep::class.java) +} diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index fbe4fa31b..cfa4b0c7a 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -26,16 +26,9 @@ import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor -import com.demonwav.mcdev.creator.custom.types.BooleanCreatorProperty -import com.demonwav.mcdev.creator.custom.types.BuildSystemCoordinatesCreatorProperty -import com.demonwav.mcdev.creator.custom.types.ClassFqnCreatorProperty -import com.demonwav.mcdev.creator.custom.types.JdkCreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty -import com.demonwav.mcdev.creator.custom.types.SemanticVersionCreatorProperty -import com.demonwav.mcdev.creator.custom.types.StringCreatorProperty -import com.demonwav.mcdev.creator.custom.types.IntegerCreatorProperty import com.demonwav.mcdev.creator.findStep import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep import com.demonwav.mcdev.util.fromJson @@ -43,9 +36,9 @@ import com.google.gson.Gson import com.intellij.ide.fileTemplates.impl.CustomFileTemplate import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData +import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory -import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager @@ -66,16 +59,8 @@ import com.intellij.util.io.readText import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent -import kotlin.collections.List -import kotlin.collections.MutableMap import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.collections.emptyList -import kotlin.collections.mapNotNull -import kotlin.collections.mapOf -import kotlin.collections.mapValuesTo -import kotlin.collections.mutableMapOf -import kotlin.collections.plusAssign import kotlin.collections.set import kotlin.io.path.absolute import kotlin.io.path.exists @@ -84,7 +69,7 @@ import kotlin.io.path.exists * The step to select a custom template repo. */ class CustomPlatformStep( - parent: PlatformTypeStep, + parent: NewProjectWizardStep, ) : AbstractLongRunningAssetsStep(parent) { override val description: String = MCDevBundle("creator.ui.custom.step.description") @@ -242,26 +227,6 @@ class CustomPlatformStep( } private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { - val baseData = data.getUserData(NewProjectWizardBaseData.KEY) - ?: return into.also { thisLogger().error("Could not find wizard base data") } - val javaVersion = findStep().preferredJdk.ordinal - val buildSystemProps = findStep>() - - into += mapOf( - "PROJECT_NAME" to baseData.name, - "JAVA_VERSION" to javaVersion, - "GROUP_ID" to buildSystemProps.groupId, - "ARTIFACT_ID" to buildSystemProps.artifactId, - "VERSION" to buildSystemProps.version, - ) - return properties.mapValuesTo(into) { (_, prop) -> prop.graphProperty.get() } } - - class TypeFactory : PlatformTypeStep.Factory { - override val name - get() = MCDevBundle("creator.ui.platform.custom.name") - - override fun createStep(parent: PlatformTypeStep) = CustomPlatformStep(parent) - } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f55a52d41..22926854f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -89,7 +89,6 @@ - @@ -196,6 +195,7 @@ + From 120a66f21ce24e5b283428e361e1fa7afce0839c Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 18:02:40 +0200 Subject: [PATCH 011/118] Add default values to template descriptor --- .../creator/custom/TemplateDescriptor.kt | 21 ++++++++++--------- .../custom/types/ExternalCreatorProperty.kt | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index e667752ab..5a39ab000 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -9,22 +9,23 @@ data class TemplatePropertyDescriptor( val name: String, val type: String, val label: String, - val order: Int?, - val options: List?, - val remember: Boolean?, - val hidden: Boolean?, - val editable: Boolean?, + val order: Int? = null, + val options: List? = null, + val groupProperties: List? = null, + val remember: Boolean? = null, + val hidden: Boolean? = null, + val editable: Boolean? = null, val default: Any, - val derives: PropertyDerivation?, - val inheritFrom: String? + val derives: PropertyDerivation? = null, + val inheritFrom: String? = null ) data class PropertyDerivation( - val parents: List?, + val parents: List? = null, val method: String, val default: Any, - val whenModified: Boolean?, - val parameters: Map?, + val whenModified: Boolean? = null, + val parameters: Map? = null, ) data class TemplateFile( diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index 573588595..e8f060660 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -10,7 +10,7 @@ class ExternalCreatorProperty( graph: PropertyGraph, properties: Map>, override val graphProperty: GraphProperty, - descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", null, null, null, null, null, "", null, null), + descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", default = ""), ) : CreatorProperty(descriptor, graph, properties) { override fun setupProperty() = Unit From 0bf51a1db28f161fb935a7d4e6b7b5df6ad83837 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 18:25:13 +0200 Subject: [PATCH 012/118] Add option to output null value if default --- .../kotlin/creator/custom/TemplateDescriptor.kt | 1 + .../kotlin/creator/custom/types/CreatorProperty.kt | 14 +++++++++++++- .../creator/platformtype/CustomPlatformStep.kt | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 5a39ab000..f9c66f436 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -16,6 +16,7 @@ data class TemplatePropertyDescriptor( val hidden: Boolean? = null, val editable: Boolean? = null, val default: Any, + val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, val inheritFrom: String? = null ) diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index d7d7dbb5b..c24df21a2 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -29,6 +29,18 @@ abstract class CreatorProperty( open fun toStringProperty(graphProperty: GraphProperty): ObservableMutableProperty = graphProperty.transform(::serialize, ::deserialize) + open fun get(): T? { + val value = graphProperty.get() + if (descriptor.nullIfDefault == true) { + val default = createDefaultValue(descriptor.default) + if (value == default) { + return null + } + } + + return value + } + /** * Produces a new value based on the provided [parentValues] and the template-defined [derivation] configuration. * @@ -60,7 +72,7 @@ abstract class CreatorProperty( } } - fun collectParentValues(): List = parents.map { properties[it]!!.graphProperty.get() } + fun collectParentValues(): List = parents.map { properties[it]!!.get() } @Suppress("UNCHECKED_CAST") graphProperty.set(derive(collectParentValues(), descriptor.derives) as T) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index cfa4b0c7a..18f749774 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -227,6 +227,6 @@ class CustomPlatformStep( } private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { - return properties.mapValuesTo(into) { (_, prop) -> prop.graphProperty.get() } + return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } } From bc88e1f305c3d84e886725f70ae8d1f3e1dca91d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 18:35:26 +0200 Subject: [PATCH 013/118] Add group/collapsibleGroup support --- .../creator/custom/TemplateDescriptor.kt | 1 + .../platformtype/CustomPlatformStep.kt | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index f9c66f436..be2e5eb71 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -15,6 +15,7 @@ data class TemplatePropertyDescriptor( val remember: Boolean? = null, val hidden: Boolean? = null, val editable: Boolean? = null, + val collapsible: Boolean? = null, val default: Any, val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 18f749774..8bc357751 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -166,6 +166,34 @@ class CustomPlatformStep( } private fun setupProperty(descriptor: TemplatePropertyDescriptor): Pair, Int>? { + if (!descriptor.groupProperties.isNullOrEmpty()) { + val childrenUiFactories = descriptor.groupProperties + .mapNotNull(::setupProperty) + .sortedBy { (_, order) -> order } + .map { it.first } + + val factory = Consumer { panel -> + if (descriptor.collapsible == false) { + panel.group(descriptor.label) { + for (childFactory in childrenUiFactories) { + childFactory.accept(this@group) + } + } + } else { + val group = panel.collapsibleGroup(descriptor.label) { + for (childFactory in childrenUiFactories) { + childFactory.accept(this@collapsibleGroup) + } + } + + group.expanded = descriptor.default as? Boolean ?: false + } + } + + val order = descriptor.order ?: 0 + return factory to order + } + if (descriptor.name in properties.keys) { thisLogger().error("Duplicate property name ${descriptor.name}") return null From 0b359447026696dceb10253bfc4b98419d5d4d31 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 20:23:27 +0200 Subject: [PATCH 014/118] Dropdown labels --- .../creator/custom/TemplateDescriptor.kt | 2 +- .../custom/types/SimpleCreatorProperty.kt | 55 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index be2e5eb71..184801333 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -10,7 +10,7 @@ data class TemplatePropertyDescriptor( val type: String, val label: String, val order: Int? = null, - val options: List? = null, + val options: Any? = null, val groupProperties: List? = null, val remember: Boolean? = null, val hidden: Boolean? = null, diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 49373f77a..c5c1abf58 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -7,6 +7,9 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem +import java.awt.Component +import javax.swing.DefaultListCellRenderer +import javax.swing.JList abstract class SimpleCreatorProperty( graph: PropertyGraph, @@ -14,17 +17,45 @@ abstract class SimpleCreatorProperty( properties: Map> ) : CreatorProperty(descriptor, graph, properties) { - private val isDropdown = !descriptor.options.isNullOrEmpty() - private val options = descriptor.options?.filterIsInstance()?.map(::deserialize) ?: emptyList() - private val defaultOptionIndex = if (isDropdown) descriptor.default as? Int ?: 0 else null - private val defaultValue by lazy { createDefaultValue(if (isDropdown) descriptor.options!![defaultOptionIndex!!] else descriptor.default) } + private val options: Map? = makeOptionsList() + + private fun makeOptionsList(): Map? { + val map = when (val options = descriptor.options) { + is Map<*, *> -> options.mapValues { it.value.toString() } + is Iterable<*> -> options.associateWithTo(linkedMapOf()) { it.toString() } + else -> null + } + + return map?.mapKeys { + @Suppress("UNCHECKED_CAST") + when (val key = it.key) { + is String -> deserialize(key) + else -> key + } as T + } + } + + private val isDropdown = !options.isNullOrEmpty() + private val defaultValue by lazy { + val raw = if (isDropdown) { + if (descriptor.default is Number && descriptor.options is List<*>) { + descriptor.options[descriptor.default.toInt()] + } else { + options!![createDefaultValue(descriptor.default)] + } + } else { + descriptor.default + } + + createDefaultValue(raw) + } override val graphProperty: GraphProperty by lazy { graph.property(defaultValue) } override fun buildUi(panel: Panel, context: WizardContext) { if (isDropdown) { panel.row(descriptor.label) { - comboBox(options) + comboBox(options!!.keys, DropdownAutoRenderer()) .bindItem(graphProperty) .enabled(descriptor.editable != false) .also { ComboboxSpeedSearch.installOn(it.component) } @@ -35,4 +66,18 @@ abstract class SimpleCreatorProperty( } abstract fun buildSimpleUi(panel: Panel, context: WizardContext) + + private inner class DropdownAutoRenderer : DefaultListCellRenderer() { + + override fun getListCellRendererComponent( + list: JList?, + value: Any?, + index: Int, + isSelected: Boolean, + cellHasFocus: Boolean + ): Component { + val label = options!![value] ?: value.toString() + return super.getListCellRendererComponent(list, label, index, isSelected, cellHasFocus) + } + } } From 334f388192d6aec4ff9e52f9fd6993db76ea7df8 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 20:40:00 +0200 Subject: [PATCH 015/118] Use segmented buttons for options by default --- .../kotlin/creator/custom/TemplateDescriptor.kt | 2 ++ .../creator/custom/types/SimpleCreatorProperty.kt | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 184801333..7060180a5 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -11,6 +11,8 @@ data class TemplatePropertyDescriptor( val label: String, val order: Int? = null, val options: Any? = null, + val maxSegmentedButtonsCount: Int? = null, + val forceDropdown: Boolean? = null, val groupProperties: List? = null, val remember: Boolean? = null, val hidden: Boolean? = null, diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index c5c1abf58..107c0ab41 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -55,10 +55,17 @@ abstract class SimpleCreatorProperty( override fun buildUi(panel: Panel, context: WizardContext) { if (isDropdown) { panel.row(descriptor.label) { - comboBox(options!!.keys, DropdownAutoRenderer()) - .bindItem(graphProperty) - .enabled(descriptor.editable != false) - .also { ComboboxSpeedSearch.installOn(it.component) } + if (descriptor.forceDropdown == true) { + comboBox(options!!.keys, DropdownAutoRenderer()) + .bindItem(graphProperty) + .enabled(descriptor.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + } else { + segmentedButton(options!!.keys) { options[it] ?: it.toString() } + .bind(graphProperty) + .enabled(descriptor.editable != false) + .maxButtonsCount(4) + } }.visible(descriptor.hidden != true) } else { buildSimpleUi(panel, context) From 28c915057465f60b90a4d82bbcc55e97d2096505 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 13 May 2024 21:58:05 +0200 Subject: [PATCH 016/118] Support comma separated string lists --- .../kotlin/creator/custom/model/StringList.kt | 10 +++++ .../types/InlineStringListCreatorProperty.kt | 42 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + 3 files changed, 53 insertions(+) create mode 100644 src/main/kotlin/creator/custom/model/StringList.kt create mode 100644 src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt diff --git a/src/main/kotlin/creator/custom/model/StringList.kt b/src/main/kotlin/creator/custom/model/StringList.kt new file mode 100644 index 000000000..9eb1c35cb --- /dev/null +++ b/src/main/kotlin/creator/custom/model/StringList.kt @@ -0,0 +1,10 @@ +package com.demonwav.mcdev.creator.custom.model + +data class StringList(val values: List) : List by values { + + override fun toString(): String = values.joinToString() + + @JvmOverloads + fun toString(separator: String, prefix: String = "", postfix: String = ""): String = + values.joinToString(separator, prefix, postfix) +} diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt new file mode 100644 index 000000000..e063eedc4 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -0,0 +1,42 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.StringList +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns + +class InlineStringListCreatorProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SimpleCreatorProperty(graph, descriptor, properties) { + + override fun createDefaultValue(raw: Any?): StringList = deserialize(raw as? String ?: "") + + override fun serialize(value: StringList): String = value.values.joinToString(transform = String::trim) + + override fun deserialize(string: String): StringList = string.split(',') + .map(String::trim) + .filter(String::isNotBlank) + .run(::StringList) + + override fun buildSimpleUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + this.textField().bindText(this@InlineStringListCreatorProperty.toStringProperty(graphProperty)) + .columns(COLUMNS_LARGE) + .enabled(descriptor.editable != false) + }.visible(descriptor.hidden != true) + } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = InlineStringListCreatorProperty(graph, descriptor, properties) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 22926854f..6e4052aba 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -132,6 +132,7 @@ + From 3e4301569b238f7306563665acc1d4ab10d98738 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Wed, 15 May 2024 00:00:58 +0200 Subject: [PATCH 017/118] Add TemplateProviders --- .../creator/custom/TemplateDescriptor.kt | 1 + .../custom/providers/LoadedTemplate.kt | 10 ++ .../custom/providers/LocalTemplateProvider.kt | 89 ++++++++++++ .../custom/providers/TemplateProvider.kt | 41 ++++++ .../custom/providers/ZipTemplateProvider.kt | 85 +++++++++++ .../custom/types/BooleanCreatorProperty.kt | 2 +- .../platformtype/CustomPlatformStep.kt | 133 ++++++++---------- src/main/resources/META-INF/plugin.xml | 4 + .../messages/MinecraftDevelopment.properties | 7 +- 9 files changed, 295 insertions(+), 77 deletions(-) create mode 100644 src/main/kotlin/creator/custom/providers/LoadedTemplate.kt create mode 100644 src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt create mode 100644 src/main/kotlin/creator/custom/providers/TemplateProvider.kt create mode 100644 src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 7060180a5..e4a644577 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -36,4 +36,5 @@ data class TemplateFile( val template: String, val destination: String, val condition: String? = null, + var contents: String = "", ) diff --git a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt new file mode 100644 index 000000000..ddebb471c --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt @@ -0,0 +1,10 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.creator.custom.TemplateDescriptor + +interface LoadedTemplate { + + val descriptor: TemplateDescriptor + + fun loadTemplateContents(path: String): String? +} diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt new file mode 100644 index 000000000..a5c40a9bf --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -0,0 +1,89 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.demonwav.mcdev.util.fromJson +import com.google.gson.Gson +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.bindStorage +import com.intellij.openapi.ui.validation.validationErrorIf +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.textValidation +import com.intellij.util.io.readText +import java.io.FileNotFoundException +import java.nio.file.Path +import java.util.function.Consumer +import javax.swing.JComponent +import kotlin.io.path.absolute +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists + +class LocalTemplateProvider : TemplateProvider { + + override fun getLabel(): String = "Local" + + override fun setupUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> LoadedTemplate> + ): JComponent { + val pathProperty = propertyGraph.property("").apply { + afterChange { path -> + provideTemplate.accept { + val root = Path.of(path.trim()).absolute() + val templateDescriptorPath = root.resolve(".mcdev.template.json") + if (!templateDescriptorPath.exists()) { + throw FileNotFoundException("Could not find template descriptor at ${templateDescriptorPath.absolutePathString()}") + } + + val descriptor = Gson().fromJson(templateDescriptorPath.readText()) + FileLoadedTemplate(root, descriptor) + } + } + bindStorage("${this@LocalTemplateProvider.javaClass.name}.path") + } + + return panel { + row(MCDevBundle("creator.ui.custom.path.label")) { + val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor().apply { + description = MCDevBundle("creator.ui.custom.path.dialog.description") + } + textFieldWithBrowseButton( + MCDevBundle("creator.ui.custom.path.dialog.title"), + context.project, + pathChooserDescriptor + ).align(AlignX.FILL) + .columns(COLUMNS_LARGE) + .bindText(pathProperty) + .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_directory")) { value -> + val file = kotlin.runCatching { + VirtualFileManager.getInstance().findFileByNioPath(Path.of(value)) + }.getOrNull() + file == null || !file.isDirectory + }) + } + } + } + + private class FileLoadedTemplate( + val root: Path, + override val descriptor: TemplateDescriptor, + ) : LoadedTemplate { + + override fun loadTemplateContents(path: String): String { + val templatePath = root.resolve(path).toAbsolutePath() + if (!templatePath.startsWith(root)) { + throw Exception("Template file path is outside of template root directory") + } + + return templatePath.readText() + } + } +} diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt new file mode 100644 index 000000000..b1216adfc --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -0,0 +1,41 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.observable.properties.PropertyGraph +import java.util.function.Consumer +import javax.swing.JComponent + +/** + * Extensions responsible for creating a [TemplateDescriptor] based on whatever data it is provided in its [UI][setupUi]. + */ +interface TemplateProvider { + + fun getLabel(): String + + fun getTooltip(): String? = null + + fun setupUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> LoadedTemplate> + ): JComponent + + companion object { + + private val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") + + fun getAll(): Collection = EP_NAME.extensionList + + fun setupAllUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> LoadedTemplate> + ) { + EP_NAME.forEachExtensionSafe { extension -> + extension.setupUi(context, propertyGraph, provideTemplate) + } + } + } +} diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt new file mode 100644 index 000000000..bc1a3845a --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -0,0 +1,85 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.demonwav.mcdev.util.fromJson +import com.google.gson.Gson +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.bindStorage +import com.intellij.openapi.ui.validation.validationErrorIf +import com.intellij.openapi.vfs.JarFileSystem +import com.intellij.openapi.vfs.readText +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.COLUMNS_LARGE +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.textValidation +import java.io.FileNotFoundException +import java.nio.file.Path +import java.util.function.Consumer +import javax.swing.JComponent +import kotlin.io.path.absolute +import kotlin.io.path.isRegularFile + +class ZipTemplateProvider : TemplateProvider { + + override fun getLabel(): String = "Archive" + + override fun setupUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> LoadedTemplate> + ): JComponent { + val pathProperty = propertyGraph.property("").apply { + afterChange { path -> + provideTemplate.accept { + val root = Path.of(path.trim()).absolute() + val descriptorText = readFromArchive(root.toString(), ".mcdev.template.json") + val descriptor = Gson().fromJson(descriptorText) + ArchiveFileLoadedTemplate(root.toString(), descriptor) + } + } + bindStorage("${this@ZipTemplateProvider.javaClass.name}.path") + } + + return panel { + row(MCDevBundle("creator.ui.custom.path.label")) { + val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor("zip").apply { + description = MCDevBundle("creator.ui.custom.archive.dialog.description") + } + textFieldWithBrowseButton( + MCDevBundle("creator.ui.custom.archive.dialog.title"), + context.project, + pathChooserDescriptor + ).align(AlignX.FILL) + .columns(COLUMNS_LARGE) + .bindText(pathProperty) + .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_file")) { value -> + !Path.of(value).isRegularFile() + }) + } + } + } + + companion object { + + private fun readFromArchive(archivePath: String, innerPath: String): String { + val fs = JarFileSystem.getInstance() + val inArchivePath = "$archivePath!/$innerPath" + val virtualFile = fs.findFileByPath(inArchivePath) + ?: throw FileNotFoundException("Could not find file $innerPath in archive $archivePath") + return virtualFile.readText() + } + } + + private class ArchiveFileLoadedTemplate( + val archivePath: String, + override val descriptor: TemplateDescriptor, + ) : LoadedTemplate { + + override fun loadTemplateContents(path: String): String = readFromArchive(archivePath, path) + } +} diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index c20b70068..219ca9c2d 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -20,7 +20,7 @@ class BooleanCreatorProperty( override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.label) { - this.checkBox(descriptor.label) + this.checkBox(descriptor.label.removeSuffix(":")) .bindSelected(graphProperty) .enabled(descriptor.editable != false) }.visible(descriptor.hidden != true) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 8bc357751..4fd273bb8 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -21,49 +21,34 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.JdkProjectSetupFinalizer -import com.demonwav.mcdev.creator.buildsystem.BuildSystemPropertiesStep -import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate +import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty -import com.demonwav.mcdev.creator.findStep import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep -import com.demonwav.mcdev.util.fromJson -import com.google.gson.Gson import com.intellij.ide.fileTemplates.impl.CustomFileTemplate import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory -import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.io.FileUtilRt -import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.ui.dsl.builder.AlignX -import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder -import com.intellij.ui.dsl.builder.bindText -import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.dsl.builder.textValidation -import com.intellij.util.io.readText import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set -import kotlin.io.path.absolute -import kotlin.io.path.exists /** * The step to select a custom template repo. @@ -74,54 +59,54 @@ class CustomPlatformStep( override val description: String = MCDevBundle("creator.ui.custom.step.description") - val pathProperty = propertyGraph.property("").apply { - bindStorage("${javaClass.name}.path") - } - var path by pathProperty + val templateProviders = TemplateProvider.getAll() + + val templateProviderProperty = propertyGraph.property(templateProviders.first()) + var templateProvider by templateProviderProperty - val descriptorProperty = propertyGraph.property(null) - var descriptor by descriptorProperty + val templateProperty = propertyGraph.property(null) + var template by templateProperty private var properties = mutableMapOf>() override fun setupUI(builder: Panel) { var taskParentComponent: JComponent? = null - builder.row(MCDevBundle("creator.ui.custom.path.label")) { - val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor().apply { - description = MCDevBundle("creator.ui.custom.path.dialog.description") - } - textFieldWithBrowseButton( - MCDevBundle("creator.ui.custom.path.dialog.title"), - context.project, - pathChooserDescriptor - ).align(AlignX.FILL) - .columns(COLUMNS_LARGE) - .bindText(pathProperty) - .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_directory")) { value -> - val file = kotlin.runCatching { - VirtualFileManager.getInstance().findFileByNioPath(Path.of(value)) - }.getOrNull() - file == null || !file.isDirectory - }) - .also { taskParentComponent = it.component } + + lateinit var templateProviderPlaceholder: Placeholder + lateinit var templatePropertyPlaceholder: Placeholder + + builder.row(MCDevBundle("creator.ui.custom.provider.label")) { + segmentedButton(templateProviders, TemplateProvider::getLabel, TemplateProvider::getTooltip) + .bind(templateProviderProperty) } builder.row { - val placeholder = placeholder().align(AlignX.FILL) - createOptionsPanelInBackground(path, placeholder, taskParentComponent) - pathProperty.afterChange { path -> - createOptionsPanelInBackground(path, placeholder, taskParentComponent) - } + templateProviderPlaceholder = placeholder() + } + + val provideTemplate = Consumer<() -> LoadedTemplate> { provider -> + createOptionsPanelInBackground(provider, templatePropertyPlaceholder, taskParentComponent) + } + + templateProviderProperty.afterChange { templateProvider -> + templatePropertyPlaceholder.component = null + templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) } + + builder.row { + templatePropertyPlaceholder = placeholder().align(AlignX.FILL) + } + + templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) } private fun createOptionsPanelInBackground( - path: String, + provider: () -> LoadedTemplate, placeholder: Placeholder, taskParentComponent: JComponent? ) { properties = mutableMapOf() - descriptor = null + template = null val baseData = data.getUserData(NewProjectWizardBaseData.KEY) ?: return thisLogger().error("Could not find wizard base data") @@ -140,7 +125,7 @@ class CustomPlatformStep( return emptyList() } - return setupTemplate(path) + return setupTemplate(provider) } } @@ -151,18 +136,18 @@ class CustomPlatformStep( } } - private fun setupTemplate(path: String): List> { - val templateDescriptorPath = Path.of(path, ".mcdev.template.json") - if (!templateDescriptorPath.exists()) { - return emptyList() + private fun setupTemplate(provider: () -> LoadedTemplate): List> { + return try { + val loadedTemplate = provider() + template = loadedTemplate + loadedTemplate.descriptor.properties + .mapNotNull { setupProperty(it) } + .sortedBy { (_, order) -> order } + .map { it.first } + } catch (e: Throwable) { + template = null + emptyList() } - - val templateDescriptor = Gson().fromJson(templateDescriptorPath.readText()) - descriptor = templateDescriptor - return templateDescriptor.properties - .mapNotNull { setupProperty(it) } - .sortedBy { (_, order) -> order } - .map { it.first } } private fun setupProperty(descriptor: TemplatePropertyDescriptor): Pair, Int>? { @@ -215,14 +200,17 @@ class CustomPlatformStep( } override fun setupAssets(project: Project) { - val descriptor = descriptor!! - - val rootPath = Path.of(path).absolute() + val template = template!! + val descriptor = template.descriptor collectTemplateProperties(assets.templateProperties) thisLogger().debug("Template properties: ${assets.templateProperties}") + val baseData = data.getUserData(NewProjectWizardBaseData.KEY) + ?: return thisLogger().error("Could not find wizard base data") + val projectPath = Path.of(baseData.path) + for (file in descriptor.files) { if (file.condition != null && !TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } @@ -232,15 +220,14 @@ class CustomPlatformStep( val relativeTemplate = TemplateEvaluator.template(assets.templateProperties, file.template).getOrNull() ?: continue - val templatePath = rootPath.resolve(relativeTemplate).toAbsolutePath() - if (!templatePath.startsWith(rootPath)) { - continue - } - val relativeDest = TemplateEvaluator.template(assets.templateProperties, file.destination).getOrNull() ?: continue - val destPath = rootPath.resolve(relativeDest).toAbsolutePath() - if (!destPath.startsWith(rootPath)) { + + val templateContents = template.loadTemplateContents(relativeTemplate) + ?: continue + + val destPath = projectPath.resolve(relativeDest).toAbsolutePath() + if (!destPath.startsWith(projectPath)) { // We want to make sure template files aren't 'escaping' the project directory continue } @@ -248,9 +235,9 @@ class CustomPlatformStep( val fileName = destPath.fileName.toString().removeSuffix(".ft") val baseFileName = FileUtilRt.getNameWithoutExtension(fileName) val extension = FileUtilRt.getExtension(fileName) - val template = CustomFileTemplate(baseFileName, extension) - template.text = templatePath.readText() - assets.addAssets(GeneratorTemplateFile(rootPath.relativize(destPath).toString(), template)) + val fileTemplate = CustomFileTemplate(baseFileName, extension) + fileTemplate.text = templateContents + assets.addAssets(GeneratorTemplateFile(projectPath.relativize(destPath).toString(), fileTemplate)) } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 6e4052aba..d46489567 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -77,6 +77,7 @@ + @@ -137,6 +138,9 @@ + + + diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 78d171568..22d83750b 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -34,12 +34,12 @@ creator.ui.platform.mod.name=Mod creator.ui.platform.plugin.name=Plugin creator.ui.custom.step.description=Creating project based on template... +creator.ui.custom.provider.label=Template Provider: creator.ui.custom.path.label=Templates Path: creator.ui.custom.path.dialog.title=Template Root creator.ui.custom.path.dialog.description=Select the root directory of the template repository - -# Used indirectly in the creator's procedural form -creator.ui.custom.property.class_fqn.label=Class Name: +creator.ui.custom.archive.dialog.title=Template Archive +creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template creator.ui.license.label=License: creator.ui.main_class.label=Main Class: @@ -82,6 +82,7 @@ creator.step.maven.import_maven.description=Importing Maven project creator.step.reformat.description=Reformatting files creator.validation.custom.path_not_a_directory=Path is not a directory +creator.validation.custom.path_not_a_file=Path is not a file creator.validation.group_id_non_example=Group ID must be changed from "org.example" creator.validation.semantic_version=Version must be a valid semantic version From 9f1eb1edc4a62c3ea1500748e90fdf5e6f787680 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 16 May 2024 16:57:42 +0200 Subject: [PATCH 018/118] WIP Sponge creator --- .../custom/types/IntegerCreatorProperty.kt | 11 ++++++++++ .../custom/types/StringCreatorProperty.kt | 22 +++++++++++++++++++ .../platformtype/CustomPlatformStep.kt | 1 + .../platform/sponge/util/SpongeVersions.kt | 22 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 src/main/kotlin/platform/sponge/util/SpongeVersions.kt diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 6a1c96bac..ba8b6f569 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -2,6 +2,8 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.platform.sponge.SpongeVersion +import com.demonwav.mcdev.platform.sponge.util.SpongeVersions import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext @@ -34,6 +36,7 @@ class IntegerCreatorProperty( override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { return when (derivation.method) { "recommendJavaVersionForMcVersion" -> recommendJavaVersionForMcVersion(parentValues[0]) + "recommendJavaVersionForSpongeApiVersion" -> recommendJavaVersionForSpongeApiVersion(parentValues[0]) else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } @@ -46,6 +49,14 @@ class IntegerCreatorProperty( return MinecraftVersions.requiredJavaVersion(from).ordinal } + private fun recommendJavaVersionForSpongeApiVersion(from: Any?): Int { + if (from !is SemanticVersion) { + return 17 + } + + return SpongeVersions.requiredJavaVersion(from).ordinal + } + class Factory : CreatorPropertyFactory { override fun create( graph: PropertyGraph, diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index a883cba83..ee486c3d0 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty @@ -23,6 +24,27 @@ class StringCreatorProperty( override fun toStringProperty(graphProperty: GraphProperty) = graphProperty + override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { + return when (derivation.method) { + "suggestSpongePluginId" -> suggestSpongePluginId(parentValues.first()) + else -> throw IllegalArgumentException("Unknown method derivation $derivation") + } + } + + private fun suggestSpongePluginId(projectName: Any?): String? { + if (projectName !is String) { + return null + } + + val invalidModIdRegex = "[^a-z0-9-_]+".toRegex() + val sanitized = projectName.lowercase().replace(invalidModIdRegex, "_") + if (sanitized.length > 64) { + return sanitized.substring(0, 64) + } + + return sanitized + } + override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.label) { this.textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 4fd273bb8..829defd52 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -146,6 +146,7 @@ class CustomPlatformStep( .map { it.first } } catch (e: Throwable) { template = null + thisLogger().error(e) emptyList() } } diff --git a/src/main/kotlin/platform/sponge/util/SpongeVersions.kt b/src/main/kotlin/platform/sponge/util/SpongeVersions.kt new file mode 100644 index 000000000..640c901d8 --- /dev/null +++ b/src/main/kotlin/platform/sponge/util/SpongeVersions.kt @@ -0,0 +1,22 @@ +package com.demonwav.mcdev.platform.sponge.util + +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.openapi.projectRoots.JavaSdkVersion + +object SpongeVersions { + + val API8 = SemanticVersion.parse("8.0.0") to JavaSdkVersion.JDK_16 + val API9 = SemanticVersion.parse("9.0.0") to JavaSdkVersion.JDK_17 + val API10 = SemanticVersion.parse("10.0.0") to JavaSdkVersion.JDK_17 + val API11 = SemanticVersion.parse("11.0.0") to JavaSdkVersion.JDK_21 + + fun requiredJavaVersion(spongeApiVersion: SemanticVersion): JavaSdkVersion { + for ((api, java) in listOf(API8, API9, API10, API11).reversed()) { + if (spongeApiVersion >= api) { + return java + } + } + + return JavaSdkVersion.JDK_17 + } +} From b84751a521171e765a13f8e20a2fb3c4789bee50 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 19 May 2024 02:16:11 +0200 Subject: [PATCH 019/118] Support built-in templates --- build.gradle.kts | 9 +++++++ .../providers/BuiltInTemplateProvider.kt | 26 +++++++++++++++++++ .../custom/providers/TemplateProvider.kt | 12 +-------- .../custom/providers/ZipTemplateProvider.kt | 16 +++++++----- src/main/resources/META-INF/plugin.xml | 1 + 5 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt diff --git a/build.gradle.kts b/build.gradle.kts index a47d000e1..cef56af74 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,6 +78,12 @@ val externalAnnotationsJar = tasks.register("externalAnnotationsJar") { archiveFileName.set("externalAnnotations.jar") } +val templatesZip = tasks.register("templatesZip") { + from("mcdev-templates") + destinationDirectory.set(layout.buildDirectory.dir("mcdev-templates")) + archiveFileName.set("templates.zip") +} + repositories { maven("https://repo.denwav.dev/repository/maven-public/") maven("https://maven.fabricmc.net/") { @@ -381,6 +387,9 @@ tasks.withType { from(externalAnnotationsJar) { into("Minecraft Development/lib/resources") } + from(templatesZip) { + into("Minecraft Development/lib/resources") + } } tasks.runIde { diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt new file mode 100644 index 000000000..64d24223f --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -0,0 +1,26 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.update.PluginUtil +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import java.util.function.Consumer +import javax.swing.JComponent +import kotlin.io.path.absolutePathString + +class BuiltInTemplateProvider : TemplateProvider { + + override fun getLabel(): String = "Built In" + + override fun setupUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> LoadedTemplate> + ): JComponent? { + provideTemplate.accept { + val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/templates.zip") + ZipTemplateProvider.loadTemplateFrom(builtinTemplatesPath.absolutePathString()) + } + + return null + } +} diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index b1216adfc..bd05cc7fb 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -20,22 +20,12 @@ interface TemplateProvider { context: WizardContext, propertyGraph: PropertyGraph, provideTemplate: Consumer<() -> LoadedTemplate> - ): JComponent + ): JComponent? companion object { private val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") fun getAll(): Collection = EP_NAME.extensionList - - fun setupAllUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> LoadedTemplate> - ) { - EP_NAME.forEachExtensionSafe { extension -> - extension.setupUi(context, propertyGraph, provideTemplate) - } - } } } diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index bc1a3845a..4ab862923 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -21,7 +21,7 @@ import java.io.FileNotFoundException import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent -import kotlin.io.path.absolute +import kotlin.io.path.absolutePathString import kotlin.io.path.isRegularFile class ZipTemplateProvider : TemplateProvider { @@ -36,10 +36,7 @@ class ZipTemplateProvider : TemplateProvider { val pathProperty = propertyGraph.property("").apply { afterChange { path -> provideTemplate.accept { - val root = Path.of(path.trim()).absolute() - val descriptorText = readFromArchive(root.toString(), ".mcdev.template.json") - val descriptor = Gson().fromJson(descriptorText) - ArchiveFileLoadedTemplate(root.toString(), descriptor) + loadTemplateFrom(path) } } bindStorage("${this@ZipTemplateProvider.javaClass.name}.path") @@ -66,6 +63,13 @@ class ZipTemplateProvider : TemplateProvider { companion object { + fun loadTemplateFrom(archivePath: String): ArchiveFileLoadedTemplate { + val absolutePath = Path.of(archivePath.trim()).absolutePathString() + val descriptorText = readFromArchive(absolutePath, ".mcdev.template.json") + val descriptor = Gson().fromJson(descriptorText) + return ArchiveFileLoadedTemplate(absolutePath, descriptor) + } + private fun readFromArchive(archivePath: String, innerPath: String): String { val fs = JarFileSystem.getInstance() val inArchivePath = "$archivePath!/$innerPath" @@ -75,7 +79,7 @@ class ZipTemplateProvider : TemplateProvider { } } - private class ArchiveFileLoadedTemplate( + class ArchiveFileLoadedTemplate( val archivePath: String, override val descriptor: TemplateDescriptor, ) : LoadedTemplate { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d46489567..3491bf0ea 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -138,6 +138,7 @@ + From 217fd08c016e0c02f24fa2b9b35e23612bae3d8d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 19 May 2024 17:20:01 +0200 Subject: [PATCH 020/118] Support multiple templates per provider --- .../providers/BuiltInTemplateProvider.kt | 4 +- .../custom/providers/EmptyLoadedTemplate.kt | 20 ++++ .../custom/providers/LoadedTemplate.kt | 3 + .../custom/providers/LocalTemplateProvider.kt | 33 +----- .../custom/providers/TemplateProvider.kt | 24 +++- .../custom/providers/VfsLoadedTemplate.kt | 21 ++++ .../custom/providers/ZipTemplateProvider.kt | 40 ++----- .../platformtype/CustomPlatformStep.kt | 107 ++++++++++++++---- .../messages/MinecraftDevelopment.properties | 1 + 9 files changed, 165 insertions(+), 88 deletions(-) create mode 100644 src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt create mode 100644 src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 64d24223f..c00f8a7d0 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -14,11 +14,11 @@ class BuiltInTemplateProvider : TemplateProvider { override fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> LoadedTemplate> + provideTemplate: Consumer<() -> Collection> ): JComponent? { provideTemplate.accept { val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/templates.zip") - ZipTemplateProvider.loadTemplateFrom(builtinTemplatesPath.absolutePathString()) + ZipTemplateProvider.loadTemplatesFrom(builtinTemplatesPath.absolutePathString()) } return null diff --git a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt new file mode 100644 index 000000000..f02b8eab6 --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt @@ -0,0 +1,20 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.creator.custom.TemplateDescriptor + +/** + * Placeholder template + */ +object EmptyLoadedTemplate : LoadedTemplate { + + override val label: String = "Empty template" + override val tooltip: String = "Empty template tooltip" + + override val descriptor: TemplateDescriptor + get() = throw UnsupportedOperationException("The empty template can't have a descriptor") + + override val isValid: Boolean = false + + override fun loadTemplateContents(path: String): String? = + throw UnsupportedOperationException("The empty template can't have contents") +} diff --git a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt index ddebb471c..612af3a1a 100644 --- a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt @@ -4,7 +4,10 @@ import com.demonwav.mcdev.creator.custom.TemplateDescriptor interface LoadedTemplate { + val label: String + val tooltip: String? val descriptor: TemplateDescriptor + val isValid: Boolean fun loadTemplateContents(path: String): String? } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index a5c40a9bf..dc559bb95 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -1,9 +1,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.custom.TemplateDescriptor -import com.demonwav.mcdev.util.fromJson -import com.google.gson.Gson +import com.demonwav.mcdev.util.virtualFile import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph @@ -16,14 +14,10 @@ import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation -import com.intellij.util.io.readText -import java.io.FileNotFoundException import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent import kotlin.io.path.absolute -import kotlin.io.path.absolutePathString -import kotlin.io.path.exists class LocalTemplateProvider : TemplateProvider { @@ -32,19 +26,13 @@ class LocalTemplateProvider : TemplateProvider { override fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> LoadedTemplate> + provideTemplate: Consumer<() -> Collection> ): JComponent { val pathProperty = propertyGraph.property("").apply { afterChange { path -> provideTemplate.accept { val root = Path.of(path.trim()).absolute() - val templateDescriptorPath = root.resolve(".mcdev.template.json") - if (!templateDescriptorPath.exists()) { - throw FileNotFoundException("Could not find template descriptor at ${templateDescriptorPath.absolutePathString()}") - } - - val descriptor = Gson().fromJson(templateDescriptorPath.readText()) - FileLoadedTemplate(root, descriptor) + root.virtualFile?.let(TemplateProvider::findTemplates) ?: emptyList() } } bindStorage("${this@LocalTemplateProvider.javaClass.name}.path") @@ -71,19 +59,4 @@ class LocalTemplateProvider : TemplateProvider { } } } - - private class FileLoadedTemplate( - val root: Path, - override val descriptor: TemplateDescriptor, - ) : LoadedTemplate { - - override fun loadTemplateContents(path: String): String { - val templatePath = root.resolve(path).toAbsolutePath() - if (!templatePath.startsWith(root)) { - throw Exception("Template file path is outside of template root directory") - } - - return templatePath.readText() - } - } } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index bd05cc7fb..7ca673284 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -1,9 +1,13 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.demonwav.mcdev.util.fromJson +import com.google.gson.Gson import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.readText import java.util.function.Consumer import javax.swing.JComponent @@ -19,7 +23,7 @@ interface TemplateProvider { fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> LoadedTemplate> + provideTemplate: Consumer<() -> Collection> ): JComponent? companion object { @@ -27,5 +31,23 @@ interface TemplateProvider { private val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") fun getAll(): Collection = EP_NAME.extensionList + + fun findTemplates( + directory: VirtualFile, + templates: MutableList = mutableListOf(), + ): List { + for (child in directory.children) { + if (child.isDirectory) { + findTemplates(child, templates) + } else if (child.name.endsWith(".mcdev.template.json")) { + val descriptor = Gson().fromJson(child.readText()) + val label = child.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) + ?: directory.presentableName + templates.add(VfsLoadedTemplate(directory, label, null, descriptor, true)) + } + } + + return templates + } } } diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt new file mode 100644 index 000000000..3b8a69f2a --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -0,0 +1,21 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.readText +import java.io.FileNotFoundException + +class VfsLoadedTemplate( + val root: VirtualFile, + override val label: String, + override val tooltip: String? = null, + override val descriptor: TemplateDescriptor, + override val isValid: Boolean, +) : LoadedTemplate { + + override fun loadTemplateContents(path: String): String? { + val virtualFile = root.fileSystem.findFileByPath(root.path + "/" + path) + ?: throw FileNotFoundException("Could not find file $path in template root ${root.path}") + return virtualFile.readText() + } +} diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 4ab862923..bf06a82d4 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -1,27 +1,22 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.custom.TemplateDescriptor -import com.demonwav.mcdev.util.fromJson -import com.google.gson.Gson +import com.intellij.ide.highlighter.ArchiveFileType import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.vfs.JarFileSystem -import com.intellij.openapi.vfs.readText import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation -import java.io.FileNotFoundException import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent -import kotlin.io.path.absolutePathString import kotlin.io.path.isRegularFile class ZipTemplateProvider : TemplateProvider { @@ -31,12 +26,12 @@ class ZipTemplateProvider : TemplateProvider { override fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> LoadedTemplate> + provideTemplate: Consumer<() -> Collection> ): JComponent { val pathProperty = propertyGraph.property("").apply { afterChange { path -> provideTemplate.accept { - loadTemplateFrom(path) + loadTemplatesFrom(path) } } bindStorage("${this@ZipTemplateProvider.javaClass.name}.path") @@ -44,7 +39,7 @@ class ZipTemplateProvider : TemplateProvider { return panel { row(MCDevBundle("creator.ui.custom.path.label")) { - val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor("zip").apply { + val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(ArchiveFileType.INSTANCE).apply { description = MCDevBundle("creator.ui.custom.archive.dialog.description") } textFieldWithBrowseButton( @@ -55,7 +50,7 @@ class ZipTemplateProvider : TemplateProvider { .columns(COLUMNS_LARGE) .bindText(pathProperty) .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_file")) { value -> - !Path.of(value).isRegularFile() + runCatching { !Path.of(value).isRegularFile() }.getOrDefault(true) }) } } @@ -63,27 +58,12 @@ class ZipTemplateProvider : TemplateProvider { companion object { - fun loadTemplateFrom(archivePath: String): ArchiveFileLoadedTemplate { - val absolutePath = Path.of(archivePath.trim()).absolutePathString() - val descriptorText = readFromArchive(absolutePath, ".mcdev.template.json") - val descriptor = Gson().fromJson(descriptorText) - return ArchiveFileLoadedTemplate(absolutePath, descriptor) - } - - private fun readFromArchive(archivePath: String, innerPath: String): String { + fun loadTemplatesFrom(archivePath: String): List { + val archiveRoot = archivePath + JarFileSystem.JAR_SEPARATOR val fs = JarFileSystem.getInstance() - val inArchivePath = "$archivePath!/$innerPath" - val virtualFile = fs.findFileByPath(inArchivePath) - ?: throw FileNotFoundException("Could not find file $innerPath in archive $archivePath") - return virtualFile.readText() + val rootFile = fs.refreshAndFindFileByPath(archiveRoot) + ?: return emptyList() + return TemplateProvider.findTemplates(rootFile) } } - - class ArchiveFileLoadedTemplate( - val archivePath: String, - override val descriptor: TemplateDescriptor, - ) : LoadedTemplate { - - override fun loadTemplateContents(path: String): String = readFromArchive(archivePath, path) - } } diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 829defd52..efecb4d13 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty @@ -34,6 +35,7 @@ import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task @@ -42,6 +44,8 @@ import com.intellij.openapi.util.io.FileUtilRt import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder +import com.intellij.ui.dsl.builder.SegmentedButton +import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import java.nio.file.Path import java.util.function.Consumer @@ -64,8 +68,12 @@ class CustomPlatformStep( val templateProviderProperty = propertyGraph.property(templateProviders.first()) var templateProvider by templateProviderProperty - val templateProperty = propertyGraph.property(null) - var template by templateProperty + val availableTemplatesProperty = propertyGraph.property>(emptyList()) + var availableTemplates by availableTemplatesProperty + lateinit var availableTemplatesSegmentedButton: SegmentedButton + + val selectedTemplateProperty = propertyGraph.property(EmptyLoadedTemplate) + var selectedTemplate by selectedTemplateProperty private var properties = mutableMapOf>() @@ -84,68 +92,117 @@ class CustomPlatformStep( templateProviderPlaceholder = placeholder() } - val provideTemplate = Consumer<() -> LoadedTemplate> { provider -> - createOptionsPanelInBackground(provider, templatePropertyPlaceholder, taskParentComponent) + val provideTemplate = Consumer<() -> Collection> { provider -> + loadTemplatesInBackground(provider, taskParentComponent) } templateProviderProperty.afterChange { templateProvider -> templatePropertyPlaceholder.component = null + availableTemplates = emptyList() + availableTemplatesSegmentedButton.items(availableTemplates) templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) } + builder.row(MCDevBundle("creator.ui.custom.templates.label")) { + availableTemplatesSegmentedButton = + segmentedButton(emptyList(), LoadedTemplate::label, LoadedTemplate::tooltip) + .bind(selectedTemplateProperty) + }.visibleIf(availableTemplatesProperty.transform { it.size > 1 }) + + availableTemplatesProperty.afterChange { newTemplates -> + availableTemplatesSegmentedButton.items(newTemplates) + templatePropertyPlaceholder.component = null + selectedTemplate = EmptyLoadedTemplate + } + + selectedTemplateProperty.afterChange { template -> + createOptionsPanelInBackground(template, templatePropertyPlaceholder, templateProviderPlaceholder.component) + } + builder.row { templatePropertyPlaceholder = placeholder().align(AlignX.FILL) - } + }.topGap(TopGap.SMALL) templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) } - private fun createOptionsPanelInBackground( - provider: () -> LoadedTemplate, - placeholder: Placeholder, + private fun loadTemplatesInBackground( + provider: () -> Collection, taskParentComponent: JComponent? ) { - properties = mutableMapOf() - template = null - - val baseData = data.getUserData(NewProjectWizardBaseData.KEY) - ?: return thisLogger().error("Could not find wizard base data") - - properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) + selectedTemplate = EmptyLoadedTemplate - val task = object : Task.WithResult>, Exception>( + val task = object : Task.WithResult, Exception>( context.project, taskParentComponent, MCDevBundle("creator.step.generic.project_created.message"), false ) { - override fun compute(indicator: ProgressIndicator): List> { + override fun compute(indicator: ProgressIndicator): Collection { if (project?.isDisposed == true) { return emptyList() } - return setupTemplate(provider) + return provider() } } + val newTemplates = ProgressManager.getInstance().run(task) + availableTemplates = newTemplates + availableTemplatesSegmentedButton.items(newTemplates) + availableTemplatesSegmentedButton.selectedItem = newTemplates.firstOrNull() + } + + private fun createOptionsPanelInBackground( + template: LoadedTemplate, + placeholder: Placeholder, + taskParentComponent: JComponent? + ) { + properties = mutableMapOf() + + if (!template.isValid) { + return + } + + val baseData = data.getUserData(NewProjectWizardBaseData.KEY) + ?: return thisLogger().error("Could not find wizard base data") + + properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) + + // val task = object : Task.WithResult>, Exception>( + // context.project, + // taskParentComponent, + // MCDevBundle("creator.step.generic.project_created.message"), + // false + // ) { + // + // override fun compute(indicator: ProgressIndicator): List> { + // if (project?.isDisposed == true) { + // return emptyList() + // } + // + // return setupTemplate(template) + // } + // } + placeholder.component = panel { - for (uiFactory in ProgressManager.getInstance().run(task)) { + for (uiFactory in setupTemplate(template)) { uiFactory.accept(this) } + // for (uiFactory in ProgressManager.getInstance().run(task)) { + // uiFactory.accept(this) + // } } } - private fun setupTemplate(provider: () -> LoadedTemplate): List> { + private fun setupTemplate(template: LoadedTemplate): List> { return try { - val loadedTemplate = provider() - template = loadedTemplate - loadedTemplate.descriptor.properties + template.descriptor.properties .mapNotNull { setupProperty(it) } .sortedBy { (_, order) -> order } .map { it.first } } catch (e: Throwable) { - template = null thisLogger().error(e) emptyList() } @@ -201,7 +258,7 @@ class CustomPlatformStep( } override fun setupAssets(project: Project) { - val template = template!! + val template = selectedTemplate val descriptor = template.descriptor collectTemplateProperties(assets.templateProperties) diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 22d83750b..7830ab791 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -35,6 +35,7 @@ creator.ui.platform.plugin.name=Plugin creator.ui.custom.step.description=Creating project based on template... creator.ui.custom.provider.label=Template Provider: +creator.ui.custom.templates.label=Templates: creator.ui.custom.path.label=Templates Path: creator.ui.custom.path.dialog.title=Template Root creator.ui.custom.path.dialog.description=Select the root directory of the template repository From 82781eb9162baddb58b9d8ee227f94aefd7a6830 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 19 May 2024 17:57:40 +0200 Subject: [PATCH 021/118] Remove commented code --- .../platformtype/CustomPlatformStep.kt | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index efecb4d13..01788365a 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -116,7 +116,7 @@ class CustomPlatformStep( } selectedTemplateProperty.afterChange { template -> - createOptionsPanelInBackground(template, templatePropertyPlaceholder, templateProviderPlaceholder.component) + createOptionsPanelInBackground(template, templatePropertyPlaceholder) } builder.row { @@ -154,11 +154,7 @@ class CustomPlatformStep( availableTemplatesSegmentedButton.selectedItem = newTemplates.firstOrNull() } - private fun createOptionsPanelInBackground( - template: LoadedTemplate, - placeholder: Placeholder, - taskParentComponent: JComponent? - ) { + private fun createOptionsPanelInBackground(template: LoadedTemplate, placeholder: Placeholder) { properties = mutableMapOf() if (!template.isValid) { @@ -170,29 +166,10 @@ class CustomPlatformStep( properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) - // val task = object : Task.WithResult>, Exception>( - // context.project, - // taskParentComponent, - // MCDevBundle("creator.step.generic.project_created.message"), - // false - // ) { - // - // override fun compute(indicator: ProgressIndicator): List> { - // if (project?.isDisposed == true) { - // return emptyList() - // } - // - // return setupTemplate(template) - // } - // } - placeholder.component = panel { for (uiFactory in setupTemplate(template)) { uiFactory.accept(this) } - // for (uiFactory in ProgressManager.getInstance().run(task)) { - // uiFactory.accept(this) - // } } } From a272f5bf7ee765d363e0749eb3fb2e975b3d8577 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 19 May 2024 21:06:19 +0200 Subject: [PATCH 022/118] Remember used templates --- .../custom/model/RecentProjectTemplates.kt | 47 +++++++++++++++++++ .../providers/BuiltInTemplateProvider.kt | 2 + .../custom/providers/EmptyLoadedTemplate.kt | 3 ++ .../custom/providers/LoadedTemplate.kt | 2 + .../custom/providers/LocalTemplateProvider.kt | 2 + .../providers/RecentTemplatesProvider.kt | 28 +++++++++++ .../custom/providers/TemplateProvider.kt | 27 +++++++++-- .../custom/providers/VfsLoadedTemplate.kt | 47 +++++++++++++++++++ .../custom/providers/ZipTemplateProvider.kt | 8 ++-- .../platformtype/CustomPlatformStep.kt | 7 +++ src/main/resources/META-INF/plugin.xml | 2 + 11 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt create mode 100644 src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt diff --git a/src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt b/src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt new file mode 100644 index 000000000..c750db02a --- /dev/null +++ b/src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt @@ -0,0 +1,47 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate +import com.intellij.openapi.components.BaseState +import com.intellij.openapi.components.SimplePersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.service +import com.intellij.util.application +import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.util.xmlb.annotations.Tag +import com.intellij.util.xmlb.annotations.Text +import com.intellij.util.xmlb.annotations.XCollection + +@State(name = "RecentProjectTemplates", storages = [Storage("minecraft_dev.xml")]) +class RecentProjectTemplates : SimplePersistentStateComponent(State()) { + + class State : BaseState() { + @get:XCollection(style = XCollection.Style.v2) + var templates by list() + } + + @Tag("template") + data class TemplateItem( + @get:Attribute("provider") + var provider: String, + @get:Text + var location: String + ) { + constructor() : this("", "") + } + + fun addNewTemplate(providerClassName: String, template: LoadedTemplate) { + val serialized = template.serialize() + ?: return + + val item = TemplateItem(providerClassName, serialized) + + state.templates.removeAll { it == item } + state.templates.add(0, item) + } + + companion object { + val instance: RecentProjectTemplates + get() = application.service() + } +} diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index c00f8a7d0..40ff45b03 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -23,4 +23,6 @@ class BuiltInTemplateProvider : TemplateProvider { return null } + + override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) } diff --git a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt index f02b8eab6..c6671bb9b 100644 --- a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt @@ -17,4 +17,7 @@ object EmptyLoadedTemplate : LoadedTemplate { override fun loadTemplateContents(path: String): String? = throw UnsupportedOperationException("The empty template can't have contents") + + override fun serialize(): String? = + throw UnsupportedOperationException("The empty template can't have contents") } diff --git a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt index 612af3a1a..b85b73949 100644 --- a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt @@ -10,4 +10,6 @@ interface LoadedTemplate { val isValid: Boolean fun loadTemplateContents(path: String): String? + + fun serialize(): String? } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index dc559bb95..5f5615ef7 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -59,4 +59,6 @@ class LocalTemplateProvider : TemplateProvider { } } } + + override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) } diff --git a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt new file mode 100644 index 000000000..59e7ce65d --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt @@ -0,0 +1,28 @@ +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import java.util.function.Consumer +import javax.swing.JComponent + +class RecentTemplatesProvider : TemplateProvider { + + override fun getLabel(): String = "Recent" + + override fun setupUi( + context: WizardContext, + propertyGraph: PropertyGraph, + provideTemplate: Consumer<() -> Collection> + ): JComponent? { + provideTemplate.accept { + RecentProjectTemplates.instance.state.templates.mapNotNull { (provider, element) -> + TemplateProvider.get(provider)?.deserializeAndLoad(element) + } + } + return null + } + + override fun deserializeAndLoad(element: String): LoadedTemplate = + throw UnsupportedOperationException("The recent templates provider is not supposed to deserialize nor load") +} diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 7ca673284..b1d453fd3 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -26,10 +26,16 @@ interface TemplateProvider { provideTemplate: Consumer<() -> Collection> ): JComponent? + fun deserializeAndLoad(element: String): LoadedTemplate? + companion object { private val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") + fun get(name: String): TemplateProvider? { + return getAll().find { it.javaClass.name == name } + } + fun getAll(): Collection = EP_NAME.extensionList fun findTemplates( @@ -40,14 +46,27 @@ interface TemplateProvider { if (child.isDirectory) { findTemplates(child, templates) } else if (child.name.endsWith(".mcdev.template.json")) { - val descriptor = Gson().fromJson(child.readText()) - val label = child.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) - ?: directory.presentableName - templates.add(VfsLoadedTemplate(directory, label, null, descriptor, true)) + templates.add(createVfsLoadedTemplate(directory, child)) } } return templates } + + fun createVfsLoadedTemplate( + root: VirtualFile, + descriptorFile: VirtualFile, + tooltip: String? = null + ): VfsLoadedTemplate { + val descriptor = Gson().fromJson(descriptorFile.readText()) + val label = descriptorFile.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) + ?: root.presentableName + return VfsLoadedTemplate(root, descriptorFile, label, tooltip, descriptor, true) + } + + fun deserializeAndLoadVfs(element: String): LoadedTemplate? { + val serialized = Gson().fromJson(element) + return serialized.load() + } } } diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index 3b8a69f2a..d55796cda 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -1,12 +1,21 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.google.gson.Gson +import com.intellij.openapi.diagnostic.getOrLogException +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.vfs.JarFileSystem +import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.readText +import com.intellij.util.xmlb.XmlSerializer import java.io.FileNotFoundException +import org.jdom.Element class VfsLoadedTemplate( val root: VirtualFile, + val descriptorFile: VirtualFile, override val label: String, override val tooltip: String? = null, override val descriptor: TemplateDescriptor, @@ -18,4 +27,42 @@ class VfsLoadedTemplate( ?: throw FileNotFoundException("Could not find file $path in template root ${root.path}") return virtualFile.readText() } + + data class Serialized( + val root: String, + val descriptorPath: String + ) { + fun load(): LoadedTemplate? { + val fs = if (root.contains(JarFileSystem.JAR_SEPARATOR)) { + JarFileSystem.getInstance() + } else { + LocalFileSystem.getInstance() + } + val rootFile = fs.refreshAndFindFileByPath(root) + ?: return null + val descriptorFile = fs.refreshAndFindFileByPath(descriptorPath) + ?: return null + val tooltip = descriptorFile.path + + return TemplateProvider.createVfsLoadedTemplate(rootFile, descriptorFile, tooltip) + } + } + + override fun serialize(): String? { + return Gson().toJson(Serialized(root.path, descriptorFile.path)) + } + + companion object { + + fun deserialize(element: Element): VfsLoadedTemplate? = runCatching { + val serialized = XmlSerializer.deserialize(element, Serialized::class.java) + + val rootFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(serialized.root) + ?: return null + val descriptorFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(serialized.descriptorPath) + ?: return null + + TemplateProvider.createVfsLoadedTemplate(rootFile, descriptorFile) + }.getOrLogException(logger()) + } } diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index bf06a82d4..b25703e15 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -39,9 +39,9 @@ class ZipTemplateProvider : TemplateProvider { return panel { row(MCDevBundle("creator.ui.custom.path.label")) { - val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(ArchiveFileType.INSTANCE).apply { - description = MCDevBundle("creator.ui.custom.archive.dialog.description") - } + val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor() + .withFileFilter { it.extension == "zip" } + .apply { description = MCDevBundle("creator.ui.custom.archive.dialog.description") } textFieldWithBrowseButton( MCDevBundle("creator.ui.custom.archive.dialog.title"), context.project, @@ -56,6 +56,8 @@ class ZipTemplateProvider : TemplateProvider { } } + override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) + companion object { fun loadTemplatesFrom(archivePath: String): List { diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt index 01788365a..e8b4cc2b9 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.platformtype import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate import com.demonwav.mcdev.creator.custom.providers.TemplateProvider @@ -236,6 +237,12 @@ class CustomPlatformStep( override fun setupAssets(project: Project) { val template = selectedTemplate + if (template is EmptyLoadedTemplate) { + return + } + + RecentProjectTemplates.instance.addNewTemplate(templateProvider.javaClass.name, template) + val descriptor = template.descriptor collectTemplateProperties(assets.templateProperties) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 3491bf0ea..1719fa0f9 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -138,6 +138,7 @@ + @@ -206,6 +207,7 @@ + From ccbc8df2b27f98d0389a16af508cc80c2500e627 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 19 May 2024 21:53:17 +0200 Subject: [PATCH 023/118] Move CustomPlatformStep to the appropriate package --- .../kotlin/creator/custom/CustomMinecraftModuleBuilder.kt | 1 - .../creator/{platformtype => custom}/CustomPlatformStep.kt | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) rename src/main/kotlin/creator/{platformtype => custom}/CustomPlatformStep.kt (98%) diff --git a/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt index 13b6dd983..60dd29e34 100644 --- a/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt +++ b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt @@ -22,7 +22,6 @@ package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.asset.PlatformAssets -import com.demonwav.mcdev.creator.platformtype.CustomPlatformStep import com.demonwav.mcdev.creator.step.NewProjectWizardChainStep.Companion.nextStep import com.intellij.ide.projectWizard.ProjectSettingsStep import com.intellij.ide.util.projectWizard.WizardContext diff --git a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt similarity index 98% rename from src/main/kotlin/creator/platformtype/CustomPlatformStep.kt rename to src/main/kotlin/creator/custom/CustomPlatformStep.kt index e8b4cc2b9..746ccc2cb 100644 --- a/src/main/kotlin/creator/platformtype/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -18,11 +18,9 @@ * along with this program. If not, see . */ -package com.demonwav.mcdev.creator.platformtype +package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.custom.TemplateEvaluator -import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate From c40b2862679c8e46f8c39b28e6152aaae2e61532 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 26 May 2024 12:18:45 +0200 Subject: [PATCH 024/118] Fix recent template provider being saved in the recent list Also always show the templates list in recent templates --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 746ccc2cb..699bd0fcf 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate +import com.demonwav.mcdev.creator.custom.providers.RecentTemplatesProvider import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory @@ -34,6 +35,7 @@ import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager @@ -106,7 +108,10 @@ class CustomPlatformStep( availableTemplatesSegmentedButton = segmentedButton(emptyList(), LoadedTemplate::label, LoadedTemplate::tooltip) .bind(selectedTemplateProperty) - }.visibleIf(availableTemplatesProperty.transform { it.size > 1 }) + }.visibleIf( + availableTemplatesProperty.transform { it.size > 1 } or + templateProviderProperty.transform { it is RecentTemplatesProvider } + ) availableTemplatesProperty.afterChange { newTemplates -> availableTemplatesSegmentedButton.items(newTemplates) @@ -239,7 +244,9 @@ class CustomPlatformStep( return } - RecentProjectTemplates.instance.addNewTemplate(templateProvider.javaClass.name, template) + if (templateProvider !is RecentTemplatesProvider) { + RecentProjectTemplates.instance.addNewTemplate(templateProvider.javaClass.name, template) + } val descriptor = template.descriptor From 93623e3d1e32ad399b35550b7a110ad12e7aacaa Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 26 May 2024 12:20:43 +0200 Subject: [PATCH 025/118] Switch BuiltInTemplateProvider to flat dir --- build.gradle.kts | 10 ++-------- .../custom/providers/BuiltInTemplateProvider.kt | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cef56af74..6b8d312e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -78,12 +78,6 @@ val externalAnnotationsJar = tasks.register("externalAnnotationsJar") { archiveFileName.set("externalAnnotations.jar") } -val templatesZip = tasks.register("templatesZip") { - from("mcdev-templates") - destinationDirectory.set(layout.buildDirectory.dir("mcdev-templates")) - archiveFileName.set("templates.zip") -} - repositories { maven("https://repo.denwav.dev/repository/maven-public/") maven("https://maven.fabricmc.net/") { @@ -387,8 +381,8 @@ tasks.withType { from(externalAnnotationsJar) { into("Minecraft Development/lib/resources") } - from(templatesZip) { - into("Minecraft Development/lib/resources") + from("mcdev-templates") { + into("Minecraft Development/lib/resources/builtin-templates") } } diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 40ff45b03..bc4d303ef 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -1,11 +1,11 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.update.PluginUtil +import com.demonwav.mcdev.util.virtualFile import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph import java.util.function.Consumer import javax.swing.JComponent -import kotlin.io.path.absolutePathString class BuiltInTemplateProvider : TemplateProvider { @@ -17,8 +17,8 @@ class BuiltInTemplateProvider : TemplateProvider { provideTemplate: Consumer<() -> Collection> ): JComponent? { provideTemplate.accept { - val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/templates.zip") - ZipTemplateProvider.loadTemplatesFrom(builtinTemplatesPath.absolutePathString()) + val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") + builtinTemplatesPath.virtualFile?.let(TemplateProvider::findTemplates) ?: emptyList() } return null From 4e512e6b759ce25c01a84b57aa8295e314839e11 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 27 May 2024 16:51:58 +0200 Subject: [PATCH 026/118] Add NeoForge specific stuff --- .../creator/custom/CustomPlatformStep.kt | 6 +- .../creator/custom/TemplateDescriptor.kt | 5 +- .../creator/custom/TemplateEvaluator.kt | 9 +- .../custom/model/HasMinecraftVersion.kt | 8 + .../creator/custom/model/NeoForgeVersions.kt | 24 ++ .../creator/custom/model/ParchmentVersions.kt | 11 + .../custom/providers/TemplateProvider.kt | 3 + .../creator/custom/types/CreatorProperty.kt | 2 +- .../custom/types/IntegerCreatorProperty.kt | 13 +- .../types/MavenArtifactVersionProperty.kt | 73 ++++++ .../custom/types/NeoForgeVersionsProperty.kt | 149 +++++++++++ .../creator/custom/types/ParchmentProperty.kt | 243 ++++++++++++++++++ .../types/SemanticVersionCreatorProperty.kt | 3 +- src/main/resources/META-INF/plugin.xml | 5 +- 14 files changed, 543 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt create mode 100644 src/main/kotlin/creator/custom/model/NeoForgeVersions.kt create mode 100644 src/main/kotlin/creator/custom/model/ParchmentVersions.kt create mode 100644 src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt create mode 100644 src/main/kotlin/creator/custom/types/ParchmentProperty.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 699bd0fcf..207a82483 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -34,6 +34,8 @@ import com.intellij.ide.fileTemplates.impl.CustomFileTemplate import com.intellij.ide.starters.local.GeneratorTemplateFile import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.diagnostic.getOrLogException +import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform @@ -148,7 +150,9 @@ class CustomPlatformStep( return emptyList() } - return provider() + return runCatching { provider() } + .getOrLogException(logger()) + ?: emptyList() } } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index e4a644577..946ab78f6 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -11,6 +11,8 @@ data class TemplatePropertyDescriptor( val label: String, val order: Int? = null, val options: Any? = null, + val sourceUrl: String? = null, + val limit: Int? = null, val maxSegmentedButtonsCount: Int? = null, val forceDropdown: Boolean? = null, val groupProperties: List? = null, @@ -21,7 +23,8 @@ data class TemplatePropertyDescriptor( val default: Any, val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, - val inheritFrom: String? = null + val inheritFrom: String? = null, + val parameters: Map? = null ) data class PropertyDerivation( diff --git a/src/main/kotlin/creator/custom/TemplateEvaluator.kt b/src/main/kotlin/creator/custom/TemplateEvaluator.kt index 620bb2e73..58c65bf39 100644 --- a/src/main/kotlin/creator/custom/TemplateEvaluator.kt +++ b/src/main/kotlin/creator/custom/TemplateEvaluator.kt @@ -1,5 +1,7 @@ package com.demonwav.mcdev.creator.custom +import com.demonwav.mcdev.util.MinecraftVersions +import com.demonwav.mcdev.util.SemanticVersion import org.apache.velocity.VelocityContext import org.apache.velocity.app.Velocity import org.apache.velocity.util.StringBuilderWriter @@ -7,7 +9,12 @@ import org.apache.velocity.util.StringBuilderWriter object TemplateEvaluator { fun evaluate(properties: Map, template: String): Result> { - val context = VelocityContext(properties) + val baseProperties = mapOf( + "SemanticVersion" to SemanticVersion, + "MinecraftVersions" to MinecraftVersions + ) + + val context = VelocityContext(baseProperties + properties) val stringWriter = StringBuilderWriter() return runCatching { Velocity.evaluate(context, stringWriter, "McDevTplExpr", template) to stringWriter.toString() diff --git a/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt new file mode 100644 index 000000000..7f10bd08c --- /dev/null +++ b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt @@ -0,0 +1,8 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.util.SemanticVersion + +interface HasMinecraftVersion { + + val minecraftVersion: SemanticVersion +} diff --git a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt new file mode 100644 index 000000000..b5717c823 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt @@ -0,0 +1,24 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.util.SemanticVersion + +data class NeoForgeVersions( + val minecraft: SemanticVersion, + val neoforge: SemanticVersion, + val neogradle: SemanticVersion, +) : HasMinecraftVersion { + override val minecraftVersion = minecraft + + val minecraftNext by lazy { + val mcNext = when (val part = minecraft.parts.getOrNull(1)) { + // Mimics the code used to get the next Minecraft version in Forge's MDK + // https://github.com/MinecraftForge/MinecraftForge/blob/0ff8a596fc1ef33d4070be89dd5cb4851f93f731/build.gradle#L884 + is SemanticVersion.Companion.VersionPart.ReleasePart -> (part.version + 1).toString() + null -> "?" + else -> part.versionString + } + + "1.$mcNext" + } + val neoforgeSpec by lazy { neoforge.parts[0].versionString } +} diff --git a/src/main/kotlin/creator/custom/model/ParchmentVersions.kt b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt new file mode 100644 index 000000000..5ef32e2fc --- /dev/null +++ b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt @@ -0,0 +1,11 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.util.SemanticVersion + +data class ParchmentVersions( + val use: Boolean, + val version: SemanticVersion, + override val minecraftVersion: SemanticVersion, + val includeOlderMcVersions: Boolean, + val includeSnapshots: Boolean, +) : HasMinecraftVersion diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index b1d453fd3..01a2387d9 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -58,6 +58,9 @@ interface TemplateProvider { descriptorFile: VirtualFile, tooltip: String? = null ): VfsLoadedTemplate { + root.refresh(false, true) + descriptorFile.refresh(false, false) + val descriptor = Gson().fromJson(descriptorFile.readText()) val label = descriptorFile.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) ?: root.presentableName diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index c24df21a2..0a57151e2 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -98,7 +98,7 @@ abstract class CreatorProperty( } } - private fun makeStorageKey(discriminator: String? = null): String { + protected fun makeStorageKey(discriminator: String? = null): String { val base = "${javaClass.name}.property.${descriptor.name}.${descriptor.type}" if (discriminator == null) { return base diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index ba8b6f569..9b10aa416 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -2,7 +2,8 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor -import com.demonwav.mcdev.platform.sponge.SpongeVersion +import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion +import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions import com.demonwav.mcdev.platform.sponge.util.SpongeVersions import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion @@ -42,11 +43,15 @@ class IntegerCreatorProperty( } private fun recommendJavaVersionForMcVersion(from: Any?): Int { - if (from !is SemanticVersion) { - return 17 + if (from is SemanticVersion) { + return MinecraftVersions.requiredJavaVersion(from).ordinal + } + + if (from is HasMinecraftVersion) { + return recommendJavaVersionForMcVersion(from.minecraftVersion) } - return MinecraftVersions.requiredJavaVersion(from).ordinal + return 17 } private fun recommendJavaVersionForSpongeApiVersion(from: Any?): Int { diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt new file mode 100644 index 000000000..823b84713 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt @@ -0,0 +1,73 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class MavenArtifactVersionProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : SemanticVersionCreatorProperty(graph, descriptor, properties) { + + override val graphProperty: GraphProperty = graph.property(SemanticVersion(emptyList())) + private val versionsProperty = graph.property>(emptyList()) + private val loadingVersionsProperty = graph.property(true) + + init { + application.executeOnPooledThread { + runBlocking { + val versions = collectMavenVersions(descriptor.sourceUrl!!) + .asSequence() + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + .take(descriptor.limit ?: 50) + .toList() + withContext(Dispatchers.Swing) { + versionsProperty.set(versions) + loadingVersionsProperty.set(false) + } + } + } + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + val combobox = comboBox(versionsProperty.get()) + .bindItem(graphProperty) + .enabled(descriptor.editable != false) + .also { ComboboxSpeedSearch.installOn(it.component) } + + cell(AsyncProcessIcon(makeStorageKey("progress"))) + .visibleIf(loadingVersionsProperty) + + versionsProperty.afterChange { versions -> + combobox.component.removeAllItems() + for (version in versions) { + combobox.component.addItem(version) + } + } + }.visible(descriptor.hidden != true) + } + + class Factory : CreatorPropertyFactory { + + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = MavenArtifactVersionProperty(graph, descriptor, properties) + } +} diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt new file mode 100644 index 000000000..4b0288d2b --- /dev/null +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -0,0 +1,149 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions +import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion +import com.demonwav.mcdev.platform.neoforge.version.NeoGradleVersion +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.util.application +import javax.swing.DefaultComboBoxModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class NeoForgeVersionsProperty( + descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val emptyVersion = SemanticVersion.release() + + private val defaultValue = createDefaultValue(descriptor.default) + + override val graphProperty: GraphProperty = graph.property(defaultValue) + var versions: NeoForgeVersions by graphProperty + + private var nfVersion: NeoForgeVersion? = null + private var previousMcVersion: SemanticVersion? = null + + private val mcVersionProperty = graphProperty.transform({ it.minecraft }, { versions.copy(minecraft = it) }) + private val mcVersionsModel = DefaultComboBoxModel() + private val nfVersionProperty = graphProperty.transform({ it.neoforge }, { versions.copy(neoforge = it) }) + private val nfVersionsModel = DefaultComboBoxModel() + private val ngVersionProperty = graphProperty.transform({ it.neogradle }, { versions.copy(neogradle = it) }) + private val ngVersionsModel = DefaultComboBoxModel() + + override fun createDefaultValue(raw: Any?): NeoForgeVersions { + if (raw is String) { + return deserialize(raw) + } + + return NeoForgeVersions(emptyVersion, emptyVersion, emptyVersion) + } + + override fun serialize(value: NeoForgeVersions): String { + return "${value.minecraft} ${value.neoforge} ${value.neogradle}" + } + + override fun deserialize(string: String): NeoForgeVersions { + val versions = string.split(' ') + .take(3) + .map { SemanticVersion.tryParse(it) ?: emptyVersion } + + return NeoForgeVersions( + versions.getOrNull(0) ?: emptyVersion, + versions.getOrNull(1) ?: emptyVersion, + versions.getOrNull(2) ?: emptyVersion, + ) + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + comboBox(mcVersionsModel) + .bindItem(mcVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + + comboBox(nfVersionsModel) + .bindItem(nfVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + + comboBox(ngVersionsModel) + .bindItem(ngVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + application.executeOnPooledThread { + runBlocking { + val neoforgeVersions = NeoForgeVersion.downloadData() + val neogradleVersions = NeoGradleVersion.downloadData() + val mcVersions = neoforgeVersions?.sortedMcVersions?.let { mcVersion -> + val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String + if (filterExpr != null) { + mcVersion.filter { version -> + val conditionProps = mapOf("MC_VERSION" to version) + TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true) + } + } else { + mcVersion + } + } + + if (neoforgeVersions != null && neogradleVersions != null && !mcVersions.isNullOrEmpty()) { + withContext(Dispatchers.Swing) { + nfVersion = neoforgeVersions + + mcVersionsModel.removeAllElements() + mcVersionsModel.addAll(mcVersions) + + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraft in mcVersions -> defaultValue.minecraft + else -> mcVersions.first() + } + mcVersionProperty.set(selectedMcVersion) + + val availableNgVersions = neogradleVersions.versions.take(descriptor.limit ?: 50) + ngVersionsModel.removeAllElements() + ngVersionsModel.addAll(availableNgVersions) + ngVersionProperty.set(availableNgVersions.firstOrNull() ?: emptyVersion) + } + } + } + } + } + + override fun setupProperty() { + super.setupProperty() + + mcVersionProperty.afterChange { mcVersion -> + if (mcVersion == previousMcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + val availableNfVersions = nfVersion!!.getNeoForgeVersions(mcVersion) + .take(descriptor.limit ?: 50) + nfVersionsModel.removeAllElements() + nfVersionsModel.addAll(availableNfVersions) + nfVersionProperty.set(availableNfVersions.firstOrNull() ?: emptyVersion) + } + } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = NeoForgeVersionsProperty(descriptor, graph, properties) + } +} diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt new file mode 100644 index 000000000..7d2e0757d --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -0,0 +1,243 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.ParchmentVersion +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion +import com.demonwav.mcdev.creator.custom.model.ParchmentVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.util.application +import javax.swing.DefaultComboBoxModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext +import org.jetbrains.kotlin.cli.common.toBooleanLenient + +class ParchmentProperty( + descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val emptyVersion = SemanticVersion.release() + + private val defaultValue = createDefaultValue(descriptor.default) + + override val graphProperty: GraphProperty = graph.property(defaultValue) + var versions: ParchmentVersions by graphProperty + + private var allParchmentVersions: List? = null + private var availableParchmentVersions: List = emptyList() + + private val useParchmentProperty = graphProperty.transform({ it.use }, { versions.copy(use = it) }) + private val versionProperty = graphProperty.transform({ it.version }, { versions.copy(version = it) }) + private val versionsModel = DefaultComboBoxModel() + private val mcVersionProperty = + graphProperty.transform({ it.minecraftVersion }, { versions.copy(minecraftVersion = it) }) + private val mcVersionsModel = DefaultComboBoxModel() + private val includeOlderMcVersionsProperty = + graphProperty.transform({ it.includeOlderMcVersions }, { versions.copy(includeOlderMcVersions = it) }) + private val includeSnapshotsProperty = + graphProperty.transform({ it.includeSnapshots }, { versions.copy(includeSnapshots = it) }) + + override fun createDefaultValue(raw: Any?): ParchmentVersions { + if (raw is String) { + return deserialize(raw) + } + + return ParchmentVersions(true, emptyVersion, emptyVersion, false, false) + } + + override fun serialize(value: ParchmentVersions): String { + return "${value.use} ${value.version} ${value.minecraftVersion} ${value.includeOlderMcVersions} ${value.includeSnapshots}" + } + + override fun deserialize(string: String): ParchmentVersions { + val segments = string.split(' ') + return ParchmentVersions( + segments.getOrNull(0)?.toBooleanLenient() ?: true, + segments.getOrNull(1)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(2)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(3).toBooleanLenient() ?: false, + segments.getOrNull(4).toBooleanLenient() ?: false, + ) + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + checkBox("Use Parchment") + .bindSelected(useParchmentProperty) + + comboBox(mcVersionsModel) + .bindItem(mcVersionProperty) + .enabledIf(useParchmentProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + + comboBox(versionsModel) + .bindItem(versionProperty) + .enabledIf(useParchmentProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + panel.row("Include") { + checkBox("Older Minecraft versions") + .bindSelected(includeOlderMcVersionsProperty) + .enabledIf(useParchmentProperty) + + checkBox("Snapshots") + .bindSelected(includeSnapshotsProperty) + .enabledIf(useParchmentProperty) + }.enabled(descriptor.editable != false) + } + + override fun setupProperty() { + super.setupProperty() + + val platformMcVersionPropertyName = descriptor.parameters?.get("minecraftVersionProperty") as? String + val platformMcVersionProperty = properties[platformMcVersionPropertyName] + if (platformMcVersionProperty != null) { + graphProperty.dependsOn(platformMcVersionProperty.graphProperty, true) { + val minecraftVersion = getPlatformMinecraftVersion() + if (minecraftVersion != null) { + graphProperty.get().copy(minecraftVersion = minecraftVersion) + } else { + graphProperty.get() + } + } + } + + var previousMcVersion: SemanticVersion? = null + mcVersionProperty.afterChange { mcVersion -> + if (mcVersion == previousMcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + refreshVersionsLists(updateMcVersions = false) + } + + var previousOlderMcVersions: Boolean? = null + includeOlderMcVersionsProperty.afterChange { newValue -> + if (previousOlderMcVersions == newValue) { + return@afterChange + } + + previousOlderMcVersions = newValue + refreshVersionsLists() + } + + var previousIncludeSnapshots: Boolean? = null + includeSnapshotsProperty.afterChange { newValue -> + if (previousIncludeSnapshots == newValue) { + return@afterChange + } + + previousIncludeSnapshots = newValue + refreshVersionsLists() + } + + application.executeOnPooledThread { + runBlocking { + val downloadedVersions = ParchmentVersion.downloadData() + + if (downloadedVersions.isNotEmpty()) { + withContext(Dispatchers.Swing) { + allParchmentVersions = downloadedVersions.sortedByDescending(ParchmentVersion::parchmentVersion) + refreshVersionsLists() + + val minecraftVersion = getPlatformMinecraftVersion() + if (minecraftVersion != null) { + mcVersionProperty.set(minecraftVersion) + } + } + } + } + } + } + + private fun refreshVersionsLists(updateMcVersions: Boolean = true) { + val includeOlderMcVersions = includeOlderMcVersionsProperty.get() + val includeSnapshots = includeSnapshotsProperty.get() + + if (updateMcVersions) { + val platformMcVersion = getPlatformMinecraftVersion() + availableParchmentVersions = allParchmentVersions + ?.filter { version -> + if (!includeOlderMcVersions && platformMcVersion != null && version.mcVersion < platformMcVersion) { + return@filter false + } + + if (!includeSnapshots && version.parchmentVersion.contains("-SNAPSHOT")) { + return@filter false + } + + return@filter true + } + ?: return + + val mcVersions = availableParchmentVersions.mapTo(mutableSetOf(), ParchmentVersion::mcVersion) + mcVersionsModel.removeAllElements() + mcVersionsModel.addAll(mcVersions) + + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraftVersion in mcVersions -> defaultValue.minecraftVersion + else -> getPlatformMinecraftVersion() ?: mcVersions.first() + } + + if (mcVersionProperty.get() != selectedMcVersion) { + mcVersionProperty.set(selectedMcVersion) + } + } + + val selectedMcVersion = mcVersionProperty.get() + val parchmentVersions = availableParchmentVersions.asSequence() + .filter { it.mcVersion == selectedMcVersion } + .mapNotNull { SemanticVersion.tryParse(it.parchmentVersion) } + .sortedDescending() + .toList() + versionsModel.removeAllElements() + versionsModel.addAll(parchmentVersions) + versionProperty.set(parchmentVersions.firstOrNull() ?: emptyVersion) + } + + private fun getPlatformMinecraftVersion(): SemanticVersion? { + // val mcVersionExtractor = descriptor.parameters?.get("minecraftVersionExtractor") as? String + // ?: return null + // + // val propValues = properties.mapValues { (_, value) -> value.get() } + // val (success, result) = TemplateEvaluator.evaluate(propValues, mcVersionExtractor).getOrNull() + // ?: return null + // + // if (!success) { + // return null + // } + // + // return SemanticVersion.tryParse(result) + + val platformMcVersionPropertyName = descriptor.parameters?.get("minecraftVersionProperty") as? String + val platformMcVersionProperty = properties[platformMcVersionPropertyName] + + return when (val version = platformMcVersionProperty?.get()) { + is SemanticVersion -> version + is HasMinecraftVersion -> version.minecraftVersion + else -> null + } + } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = ParchmentProperty(descriptor, graph, properties) + } +} diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index ee4294e1d..39d2056cc 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -2,7 +2,6 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor -import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph @@ -11,7 +10,7 @@ import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns -class SemanticVersionCreatorProperty( +open class SemanticVersionCreatorProperty( graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, properties: Map> diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1719fa0f9..22b6777dc 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -132,9 +132,12 @@ - + + + + From 427d2413bb8bedd72e3b8bf60522c8d8e11c6b55 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 27 May 2024 17:02:07 +0200 Subject: [PATCH 027/118] Add TemplateApi marker annotation for template models --- .../kotlin/creator/custom/model/BuildSystemCoordinates.kt | 1 + src/main/kotlin/creator/custom/model/ClassFqn.kt | 1 + src/main/kotlin/creator/custom/model/CreatorJdk.kt | 1 + .../kotlin/creator/custom/model/HasMinecraftVersion.kt | 1 + src/main/kotlin/creator/custom/model/NeoForgeVersions.kt | 1 + src/main/kotlin/creator/custom/model/ParchmentVersions.kt | 1 + src/main/kotlin/creator/custom/model/StringList.kt | 1 + src/main/kotlin/creator/custom/model/TemplateApi.kt | 8 ++++++++ 8 files changed, 15 insertions(+) create mode 100644 src/main/kotlin/creator/custom/model/TemplateApi.kt diff --git a/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt index 0a1f182a6..2854cebf6 100644 --- a/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt +++ b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.model +@TemplateApi data class BuildSystemCoordinates(val groupId: String, val artifactId: String, val version: String) { override fun toString(): String = "$groupId:$artifactId:$version" diff --git a/src/main/kotlin/creator/custom/model/ClassFqn.kt b/src/main/kotlin/creator/custom/model/ClassFqn.kt index 8641b954d..54cd457f2 100644 --- a/src/main/kotlin/creator/custom/model/ClassFqn.kt +++ b/src/main/kotlin/creator/custom/model/ClassFqn.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.model +@TemplateApi data class ClassFqn(val fqn: String) { val className by lazy { fqn.substringAfterLast('.') } diff --git a/src/main/kotlin/creator/custom/model/CreatorJdk.kt b/src/main/kotlin/creator/custom/model/CreatorJdk.kt index 4bc17dd93..06278c956 100644 --- a/src/main/kotlin/creator/custom/model/CreatorJdk.kt +++ b/src/main/kotlin/creator/custom/model/CreatorJdk.kt @@ -3,6 +3,7 @@ package com.demonwav.mcdev.creator.custom.model import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.Sdk +@TemplateApi data class CreatorJdk(val sdk: Sdk?) { val javaVersion: Int diff --git a/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt index 7f10bd08c..4f3c82a4c 100644 --- a/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt +++ b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt @@ -2,6 +2,7 @@ package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion +@TemplateApi interface HasMinecraftVersion { val minecraftVersion: SemanticVersion diff --git a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt index b5717c823..a505bc183 100644 --- a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt +++ b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt @@ -2,6 +2,7 @@ package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion +@TemplateApi data class NeoForgeVersions( val minecraft: SemanticVersion, val neoforge: SemanticVersion, diff --git a/src/main/kotlin/creator/custom/model/ParchmentVersions.kt b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt index 5ef32e2fc..4bde631e0 100644 --- a/src/main/kotlin/creator/custom/model/ParchmentVersions.kt +++ b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt @@ -2,6 +2,7 @@ package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion +@TemplateApi data class ParchmentVersions( val use: Boolean, val version: SemanticVersion, diff --git a/src/main/kotlin/creator/custom/model/StringList.kt b/src/main/kotlin/creator/custom/model/StringList.kt index 9eb1c35cb..ea62731c5 100644 --- a/src/main/kotlin/creator/custom/model/StringList.kt +++ b/src/main/kotlin/creator/custom/model/StringList.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.model +@TemplateApi data class StringList(val values: List) : List by values { override fun toString(): String = values.joinToString() diff --git a/src/main/kotlin/creator/custom/model/TemplateApi.kt b/src/main/kotlin/creator/custom/model/TemplateApi.kt new file mode 100644 index 000000000..98dd00d74 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/TemplateApi.kt @@ -0,0 +1,8 @@ +package com.demonwav.mcdev.creator.custom.model + +/** + * Marker annotation indicating classes exposed to templates. + * + * Be careful of not breaking source or binary compatibility of those APIs without a good reason. + */ +annotation class TemplateApi From 68c563ed9aec4d21817e2b7d98463e7acf0daf5d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 27 May 2024 17:02:51 +0200 Subject: [PATCH 028/118] Move RecentProjectTemplates out of the models package --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 1 - .../kotlin/creator/custom/{model => }/RecentProjectTemplates.kt | 2 +- .../kotlin/creator/custom/providers/RecentTemplatesProvider.kt | 2 +- src/main/resources/META-INF/plugin.xml | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) rename src/main/kotlin/creator/custom/{model => }/RecentProjectTemplates.kt (96%) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 207a82483..fa3c7ade8 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -21,7 +21,6 @@ package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate import com.demonwav.mcdev.creator.custom.providers.RecentTemplatesProvider diff --git a/src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt b/src/main/kotlin/creator/custom/RecentProjectTemplates.kt similarity index 96% rename from src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt rename to src/main/kotlin/creator/custom/RecentProjectTemplates.kt index c750db02a..7e942bfc2 100644 --- a/src/main/kotlin/creator/custom/model/RecentProjectTemplates.kt +++ b/src/main/kotlin/creator/custom/RecentProjectTemplates.kt @@ -1,4 +1,4 @@ -package com.demonwav.mcdev.creator.custom.model +package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate import com.intellij.openapi.components.BaseState diff --git a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt index 59e7ce65d..a1bbd94e5 100644 --- a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt @@ -1,6 +1,6 @@ package com.demonwav.mcdev.creator.custom.providers -import com.demonwav.mcdev.creator.custom.model.RecentProjectTemplates +import com.demonwav.mcdev.creator.custom.RecentProjectTemplates import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph import java.util.function.Consumer diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 22b6777dc..16cbdb4d3 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -210,7 +210,7 @@ - + From b7c0af6580efc2f24effcf4ed2a9057208bdf5a4 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 27 May 2024 17:06:01 +0200 Subject: [PATCH 029/118] Remove old commented code --- .../creator/custom/types/ParchmentProperty.kt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index 7d2e0757d..1868cfdd7 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -210,19 +210,6 @@ class ParchmentProperty( } private fun getPlatformMinecraftVersion(): SemanticVersion? { - // val mcVersionExtractor = descriptor.parameters?.get("minecraftVersionExtractor") as? String - // ?: return null - // - // val propValues = properties.mapValues { (_, value) -> value.get() } - // val (success, result) = TemplateEvaluator.evaluate(propValues, mcVersionExtractor).getOrNull() - // ?: return null - // - // if (!success) { - // return null - // } - // - // return SemanticVersion.tryParse(result) - val platformMcVersionPropertyName = descriptor.parameters?.get("minecraftVersionProperty") as? String val platformMcVersionProperty = properties[platformMcVersionPropertyName] From b79a8333482d40068a09a2958ce5fea2e233310f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 28 May 2024 19:09:40 +0200 Subject: [PATCH 030/118] Replace usage of kotlin plugin function by stdlib one --- src/main/kotlin/creator/custom/types/ParchmentProperty.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index 1868cfdd7..31f4f77df 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -import org.jetbrains.kotlin.cli.common.toBooleanLenient class ParchmentProperty( descriptor: TemplatePropertyDescriptor, @@ -63,11 +62,11 @@ class ParchmentProperty( override fun deserialize(string: String): ParchmentVersions { val segments = string.split(' ') return ParchmentVersions( - segments.getOrNull(0)?.toBooleanLenient() ?: true, + segments.getOrNull(0)?.toBoolean() ?: true, segments.getOrNull(1)?.let(SemanticVersion::tryParse) ?: emptyVersion, segments.getOrNull(2)?.let(SemanticVersion::tryParse) ?: emptyVersion, - segments.getOrNull(3).toBooleanLenient() ?: false, - segments.getOrNull(4).toBooleanLenient() ?: false, + segments.getOrNull(3).toBoolean(), + segments.getOrNull(4).toBoolean(), ) } From 37c12e3578a22a43463d11e01dec7289d4bebb84 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Wed, 29 May 2024 18:59:40 +0200 Subject: [PATCH 031/118] Always refresh template files --- src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index d55796cda..0716330a3 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -25,6 +25,7 @@ class VfsLoadedTemplate( override fun loadTemplateContents(path: String): String? { val virtualFile = root.fileSystem.findFileByPath(root.path + "/" + path) ?: throw FileNotFoundException("Could not find file $path in template root ${root.path}") + virtualFile.refresh(false, false) return virtualFile.readText() } From fe7626518b20e658cfde1e5a4f91c8330baebf0e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Wed, 29 May 2024 19:18:51 +0200 Subject: [PATCH 032/118] Add fabric_versions --- .../creator/custom/TemplateDescriptor.kt | 1 - .../custom/model/FabricVersionsModel.kt | 16 ++ .../custom/types/FabricVersionsProperty.kt | 252 ++++++++++++++++++ .../types/MavenArtifactVersionProperty.kt | 5 +- .../platform/fabric/util/FabricVersions.kt | 2 + src/main/resources/META-INF/plugin.xml | 1 + 6 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/creator/custom/model/FabricVersionsModel.kt create mode 100644 src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 946ab78f6..5eb9cb9c2 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -11,7 +11,6 @@ data class TemplatePropertyDescriptor( val label: String, val order: Int? = null, val options: Any? = null, - val sourceUrl: String? = null, val limit: Int? = null, val maxSegmentedButtonsCount: Int? = null, val forceDropdown: Boolean? = null, diff --git a/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt new file mode 100644 index 000000000..06f82d706 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt @@ -0,0 +1,16 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.platform.fabric.creator.FabricMcVersion +import com.demonwav.mcdev.platform.fabric.util.FabricVersions +import com.demonwav.mcdev.util.SemanticVersion + +@TemplateApi +data class FabricVersionsModel( + override val minecraftVersion: SemanticVersion, + val loom: SemanticVersion, + val loader: SemanticVersion, + val yarn: FabricVersions.YarnVersion, + val useFabricApi: Boolean, + val fabricApi: SemanticVersion, + val useOfficialMappings: Boolean, +) : HasMinecraftVersion diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt new file mode 100644 index 000000000..37f81b83d --- /dev/null +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -0,0 +1,252 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.FabricVersionsModel +import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions +import com.demonwav.mcdev.platform.fabric.util.FabricVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.demonwav.mcdev.util.asyncIO +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.bindBooleanStorage +import com.intellij.openapi.observable.util.not +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.util.application +import javax.swing.DefaultComboBoxModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class FabricVersionsProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val emptyVersion = SemanticVersion.release() + private val emptyValue = FabricVersionsModel( + emptyVersion, + emptyVersion, + emptyVersion, + FabricVersions.YarnVersion("", -1), + true, + emptyVersion, + false, + ) + private val defaultValue = createDefaultValue(descriptor.default) + + private var fabricVersions: FabricVersions? = null + private var loomVersions: List? = null + private var fabricApiVersions: FabricApiVersions? = null + + override val graphProperty: GraphProperty = graph.property(defaultValue) + var model: FabricVersionsModel by graphProperty + + val mcVersionProperty = graphProperty.transform({ it.minecraftVersion }, { model.copy(minecraftVersion = it) }) + val mcVersionModel = DefaultComboBoxModel() + val showMcSnapshotsProperty = graph.property(false) + .bindBooleanStorage(makeStorageKey("showMcSnapshots")) + + val loomVersionProperty = graphProperty.transform({ it.loom }, { model.copy(loom = it) }) + val loomVersionModel = DefaultComboBoxModel() + + val loaderVersionProperty = graphProperty.transform({ it.loader }, { model.copy(loader = it) }) + val loaderVersionModel = DefaultComboBoxModel() + + val yarnVersionProperty = graphProperty.transform({ it.yarn }, { model.copy(yarn = it) }) + val yarnVersionModel = DefaultComboBoxModel() + + val fabricApiVersionProperty = graphProperty.transform({ it.fabricApi }, { model.copy(fabricApi = it) }) + val fabricApiVersionModel = DefaultComboBoxModel() + val useFabricApiVersionProperty = graphProperty.transform({ it.useFabricApi }, { model.copy(useFabricApi = it) }) + + val useOfficialMappingsProperty = + graphProperty.transform({ it.useOfficialMappings }, { model.copy(useOfficialMappings = it) }) + + override fun createDefaultValue(raw: Any?): FabricVersionsModel = when (raw) { + is String -> deserialize(raw) + else -> emptyValue + } + + override fun serialize(value: FabricVersionsModel): String { + return "${value.minecraftVersion} ${value.loom} ${value.loader} ${value.yarn} ${value.useFabricApi} ${value.fabricApi} ${value.useOfficialMappings}" + } + + override fun deserialize(string: String): FabricVersionsModel { + val segments = string.split(' ') + val yarnSegments = segments.getOrNull(3)?.split(':') + val yarnVersion = if (yarnSegments != null && yarnSegments.size == 2) { + FabricVersions.YarnVersion(yarnSegments[0], yarnSegments[1].toInt()) + } else { + emptyValue.yarn + } + return FabricVersionsModel( + segments.getOrNull(0)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(1)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(2)?.let(SemanticVersion::tryParse) ?: emptyVersion, + yarnVersion, + segments.getOrNull(4).toBoolean(), + segments.getOrNull(5)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(6).toBoolean(), + ) + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("Minecraft Version:") { + comboBox(mcVersionModel) + .bindItem(mcVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + + checkBox("Show snapshots") + .bindSelected(showMcSnapshotsProperty) + }.enabled(descriptor.editable != false) + + panel.row("Loom Version:") { + comboBox(loomVersionModel) + .bindItem(loomVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + panel.row("Loader Version:") { + comboBox(loaderVersionModel) + .bindItem(loaderVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + panel.row("Yarn Version:") { + comboBox(yarnVersionModel) + .bindItem(yarnVersionProperty) + .enabledIf(useOfficialMappingsProperty.not()) + .also { ComboboxSpeedSearch.installOn(it.component) } + + checkBox("Use official mappings") + .bindSelected(useOfficialMappingsProperty) + }.enabled(descriptor.editable != false) + + panel.row("FabricApi Version:") { + comboBox(fabricApiVersionModel) + .bindItem(fabricApiVersionProperty) + .enabledIf(useFabricApiVersionProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + + checkBox("Use FabricApi") + .bindSelected(useFabricApiVersionProperty) + }.enabled(descriptor.editable != false) + } + + override fun setupProperty() { + super.setupProperty() + + showMcSnapshotsProperty.afterChange { updateMcVersionsList() } + + var previousMcVersion: SemanticVersion? = null + mcVersionProperty.afterChange { mcVersion -> + if (previousMcVersion == mcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + updateYarnVersions() + updateFabricApiVersions() + } + + application.executeOnPooledThread { + runBlocking { + val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } + val loomVersionsJob = asyncIO { collectMavenVersions("https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml") } + val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } + + this@FabricVersionsProperty.fabricVersions = fabricVersionsJob.await() + this@FabricVersionsProperty.loomVersions = loomVersionsJob.await() + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + this@FabricVersionsProperty.fabricApiVersions = fabricApiVersionsJob.await() + + withContext(Dispatchers.Swing) { + val fabricVersions = fabricVersions + if (fabricVersions != null) { + loaderVersionModel.removeAllElements() + loaderVersionModel.addAll(fabricVersions.loader) + loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) + + updateMcVersionsList() + } + + val loomVersions = loomVersions + if (loomVersions != null) { + loomVersionModel.removeAllElements() + loomVersionModel.addAll(loomVersions) + val defaultValue = loomVersions.firstOrNull { + it.parts.none { it is SemanticVersion.Companion.VersionPart.PreReleasePart } + } ?: loomVersions.firstOrNull() ?: emptyVersion + + loomVersionProperty.set(defaultValue) + } + } + } + } + } + + private fun updateMcVersionsList() { + val versions = fabricVersions + ?: return + + val showSnapshots = showMcSnapshotsProperty.get() + val mcVersions = versions.game.asSequence() + .filter { showSnapshots || it.stable } + .mapNotNull { version -> SemanticVersion.tryParse(version.version) } + .toList() + + mcVersionModel.removeAllElements() + mcVersionModel.addAll(mcVersions) + mcVersionProperty.set(mcVersions.firstOrNull() ?: emptyVersion) + } + + private fun updateYarnVersions() { + val fabricVersions = fabricVersions + ?: return + + val mcVersion = mcVersionProperty.get() + val mcVersionString = mcVersion.toString() + + val yarnVersions = fabricVersions.mappings.asSequence() + .filter { it.gameVersion == mcVersionString } + .map { it.version } + .toList() + yarnVersionModel.removeAllElements() + yarnVersionModel.addAll(yarnVersions) + yarnVersionProperty.set(yarnVersions.firstOrNull() ?: emptyValue.yarn) + } + + private fun updateFabricApiVersions() { + val fabricApiVersions = fabricApiVersions + ?: return + + val mcVersion = mcVersionProperty.get() + val mcVersionString = mcVersion.toString() + + val apiVersions = fabricApiVersions.versions.asSequence() + .filter { mcVersionString in it.gameVersions } + .map(FabricApiVersions.Version::version) + .toList() + fabricApiVersionModel.removeAllElements() + fabricApiVersionModel.addAll(apiVersions) + fabricApiVersionProperty.set(apiVersions.firstOrNull() ?: emptyVersion) + } + + class Factory : CreatorPropertyFactory { + + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = FabricVersionsProperty(graph, descriptor, properties) + } +} diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt index 823b84713..0aca9f114 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt @@ -22,6 +22,9 @@ class MavenArtifactVersionProperty( properties: Map> ) : SemanticVersionCreatorProperty(graph, descriptor, properties) { + val sourceUrl: String + get() = descriptor.parameters!!["sourceUrl"] as String + override val graphProperty: GraphProperty = graph.property(SemanticVersion(emptyList())) private val versionsProperty = graph.property>(emptyList()) private val loadingVersionsProperty = graph.property(true) @@ -29,7 +32,7 @@ class MavenArtifactVersionProperty( init { application.executeOnPooledThread { runBlocking { - val versions = collectMavenVersions(descriptor.sourceUrl!!) + val versions = collectMavenVersions(sourceUrl) .asSequence() .mapNotNull(SemanticVersion::tryParse) .sortedDescending() diff --git a/src/main/kotlin/platform/fabric/util/FabricVersions.kt b/src/main/kotlin/platform/fabric/util/FabricVersions.kt index 9e1a03167..7b898ad78 100644 --- a/src/main/kotlin/platform/fabric/util/FabricVersions.kt +++ b/src/main/kotlin/platform/fabric/util/FabricVersions.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.platform.fabric.util +import com.demonwav.mcdev.creator.custom.model.TemplateApi import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.SemanticVersion @@ -36,6 +37,7 @@ class FabricVersions(val game: List, val mappings: List, val loa class Game(val version: String, val stable: Boolean) class Mappings(val gameVersion: String, val version: YarnVersion) + @TemplateApi class YarnVersion(val name: String, val build: Int) : Comparable { override fun toString() = name override fun compareTo(other: YarnVersion) = build.compareTo(other.build) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 16cbdb4d3..272fc160c 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -132,6 +132,7 @@ + From 445a36bb75403e4f1e31d3ba51b41d4ca5cad885 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 30 May 2024 02:09:41 +0200 Subject: [PATCH 033/118] Add license property --- .../creator/custom/model/LicenseData.kt | 9 ++++ .../creator/custom/types/LicenseProperty.kt | 54 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + 3 files changed, 64 insertions(+) create mode 100644 src/main/kotlin/creator/custom/model/LicenseData.kt create mode 100644 src/main/kotlin/creator/custom/types/LicenseProperty.kt diff --git a/src/main/kotlin/creator/custom/model/LicenseData.kt b/src/main/kotlin/creator/custom/model/LicenseData.kt new file mode 100644 index 000000000..286d1865d --- /dev/null +++ b/src/main/kotlin/creator/custom/model/LicenseData.kt @@ -0,0 +1,9 @@ +package com.demonwav.mcdev.creator.custom.model + +import java.time.ZonedDateTime + +@TemplateApi +data class LicenseData( + val id: String, + val year: String = ZonedDateTime.now().year.toString(), +) diff --git a/src/main/kotlin/creator/custom/types/LicenseProperty.kt b/src/main/kotlin/creator/custom/types/LicenseProperty.kt new file mode 100644 index 000000000..7c5fdebcb --- /dev/null +++ b/src/main/kotlin/creator/custom/types/LicenseProperty.kt @@ -0,0 +1,54 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.LicenseData +import com.demonwav.mcdev.util.License +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.EnumComboBoxModel +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import java.time.ZonedDateTime + +class LicenseProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + override val graphProperty: GraphProperty = + graph.property(createDefaultValue(descriptor.default)) + + override fun createDefaultValue(raw: Any?): LicenseData = + deserialize(raw as? String ?: License.ALL_RIGHTS_RESERVED.id) + + override fun serialize(value: LicenseData): String = value.id + + override fun deserialize(string: String): LicenseData = + LicenseData(string, ZonedDateTime.now().year.toString()) + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + val model = EnumComboBoxModel(License::class.java) + val licenseEnumProperty = graphProperty.transform( + { License.byId(it.id) ?: License.entries.first() }, + { deserialize(it.id) } + ) + comboBox(model) + .bindItem(licenseEnumProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + } + + class Factory : CreatorPropertyFactory { + + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = LicenseProperty(graph, descriptor, properties) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 272fc160c..c853cdf3f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -138,6 +138,7 @@ + From 3e245fd6e518bec616a2f29feaa9abc03625cf3d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 30 May 2024 02:11:15 +0200 Subject: [PATCH 034/118] Handle template descriptor deserialization errors --- .../creator/custom/providers/TemplateProvider.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 01a2387d9..87609973c 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -4,6 +4,8 @@ import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.util.fromJson import com.google.gson.Gson import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.Attachment +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.vfs.VirtualFile @@ -46,7 +48,16 @@ interface TemplateProvider { if (child.isDirectory) { findTemplates(child, templates) } else if (child.name.endsWith(".mcdev.template.json")) { - templates.add(createVfsLoadedTemplate(directory, child)) + try { + templates.add(createVfsLoadedTemplate(directory, child)) + } catch (e: Throwable) { + val attachment = runCatching { Attachment(child.name, child.readText()) }.getOrNull() + if (attachment != null) { + thisLogger().error("Failed to load template ${child.path}", e, attachment) + } else { + thisLogger().error("Failed to load template ${child.path}", e) + } + } } } From e10f27a9e7003285870e2b728de4de4aa572913d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 30 May 2024 02:49:59 +0200 Subject: [PATCH 035/118] Basic template inheritance and template labels --- .../creator/custom/TemplateDescriptor.kt | 3 ++ .../custom/providers/TemplateProvider.kt | 28 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 5eb9cb9c2..4e994fc56 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -1,6 +1,9 @@ package com.demonwav.mcdev.creator.custom data class TemplateDescriptor( + val label: String? = null, + val inherit: String? = null, + val hidden: Boolean? = null, val properties: List, val files: List, ) diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 87609973c..9c11588c9 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -44,12 +44,13 @@ interface TemplateProvider { directory: VirtualFile, templates: MutableList = mutableListOf(), ): List { + directory.refresh(false, true) for (child in directory.children) { if (child.isDirectory) { findTemplates(child, templates) } else if (child.name.endsWith(".mcdev.template.json")) { try { - templates.add(createVfsLoadedTemplate(directory, child)) + createVfsLoadedTemplate(directory, child)?.let(templates::add) } catch (e: Throwable) { val attachment = runCatching { Attachment(child.name, child.readText()) }.getOrNull() if (attachment != null) { @@ -68,13 +69,32 @@ interface TemplateProvider { root: VirtualFile, descriptorFile: VirtualFile, tooltip: String? = null - ): VfsLoadedTemplate { + ): VfsLoadedTemplate? { root.refresh(false, true) descriptorFile.refresh(false, false) - val descriptor = Gson().fromJson(descriptorFile.readText()) - val label = descriptorFile.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) + var descriptor = Gson().fromJson(descriptorFile.readText()) + if (descriptor.hidden == true) { + return null + } + + val label = descriptor.label + ?: descriptorFile.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) ?: root.presentableName + + if (descriptor.inherit != null) { + val parent = root.findFileByRelativePath(descriptor.inherit!!) + if (parent != null) { + parent.refresh(false, false) + val parentDescriptor = Gson().fromJson(parent.readText()) + val mergedProperties = parentDescriptor.properties + descriptor.properties + val mergedFiles = parentDescriptor.files + descriptor.files + descriptor = descriptor.copy(properties = mergedProperties, files = mergedFiles) + } else { + thisLogger().error("Could not find inherited template descriptor ${descriptor.inherit} from ${descriptorFile.path}") + } + } + return VfsLoadedTemplate(root, descriptorFile, label, tooltip, descriptor, true) } From 1f7a0fe76df91b26b0cb69b68b482f41252a7a85 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 31 May 2024 16:24:24 +0200 Subject: [PATCH 036/118] Add basic versioning --- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 1 + src/main/kotlin/creator/custom/providers/TemplateProvider.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 4e994fc56..804ebe024 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -1,6 +1,7 @@ package com.demonwav.mcdev.creator.custom data class TemplateDescriptor( + val version: Int, val label: String? = null, val inherit: String? = null, val hidden: Boolean? = null, diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 9c11588c9..97d06c9f2 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -74,6 +74,10 @@ interface TemplateProvider { descriptorFile.refresh(false, false) var descriptor = Gson().fromJson(descriptorFile.readText()) + if (descriptor.version != 1) { + return null + } + if (descriptor.hidden == true) { return null } From e5075dacd59f4e71077de4f2387f49254afa6221 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 31 May 2024 17:24:28 +0200 Subject: [PATCH 037/118] Display all yarn/fabric api versions if none match the selected game version --- .../custom/types/FabricVersionsProperty.kt | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index 37f81b83d..654b8e1ec 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.observable.util.bindBooleanStorage import com.intellij.openapi.observable.util.not import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.JBColor import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected @@ -62,10 +63,22 @@ class FabricVersionsProperty( val yarnVersionProperty = graphProperty.transform({ it.yarn }, { model.copy(yarn = it) }) val yarnVersionModel = DefaultComboBoxModel() + val yarnHasMatchingGameVersion = mcVersionProperty.transform { mcVersion -> + val versions = fabricVersions + ?: return@transform true + val mcVersionString = mcVersion.toString() + versions.mappings.any { it.gameVersion == mcVersionString } + } val fabricApiVersionProperty = graphProperty.transform({ it.fabricApi }, { model.copy(fabricApi = it) }) val fabricApiVersionModel = DefaultComboBoxModel() val useFabricApiVersionProperty = graphProperty.transform({ it.useFabricApi }, { model.copy(useFabricApi = it) }) + val fabricApiHasMatchingGameVersion = mcVersionProperty.transform { mcVersion -> + val apiVersions = fabricApiVersions + ?: return@transform true + val mcVersionString = mcVersion.toString() + apiVersions.versions.any { mcVersionString in it.gameVersions } + } val useOfficialMappingsProperty = graphProperty.transform({ it.useOfficialMappings }, { model.copy(useOfficialMappings = it) }) @@ -128,6 +141,10 @@ class FabricVersionsProperty( checkBox("Use official mappings") .bindSelected(useOfficialMappingsProperty) + + label("Unable to match Yarn versions to Minecraft version") + .visibleIf(yarnHasMatchingGameVersion.not()) + .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) panel.row("FabricApi Version:") { @@ -138,6 +155,9 @@ class FabricVersionsProperty( checkBox("Use FabricApi") .bindSelected(useFabricApiVersionProperty) + label("Unable to match API versions to Minecraft version") + .visibleIf(fabricApiHasMatchingGameVersion.not()) + .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) } @@ -216,10 +236,14 @@ class FabricVersionsProperty( val mcVersion = mcVersionProperty.get() val mcVersionString = mcVersion.toString() - val yarnVersions = fabricVersions.mappings.asSequence() - .filter { it.gameVersion == mcVersionString } - .map { it.version } - .toList() + val yarnVersions = if (yarnHasMatchingGameVersion.get()) { + fabricVersions.mappings.asSequence() + .filter { it.gameVersion == mcVersionString } + .map { it.version } + .toList() + } else { + fabricVersions.mappings.map { it.version } + } yarnVersionModel.removeAllElements() yarnVersionModel.addAll(yarnVersions) yarnVersionProperty.set(yarnVersions.firstOrNull() ?: emptyValue.yarn) @@ -232,10 +256,14 @@ class FabricVersionsProperty( val mcVersion = mcVersionProperty.get() val mcVersionString = mcVersion.toString() - val apiVersions = fabricApiVersions.versions.asSequence() - .filter { mcVersionString in it.gameVersions } - .map(FabricApiVersions.Version::version) - .toList() + val apiVersions = if (fabricApiHasMatchingGameVersion.get()) { + fabricApiVersions.versions.asSequence() + .filter { mcVersionString in it.gameVersions } + .map(FabricApiVersions.Version::version) + .toList() + } else { + fabricApiVersions.versions.map(FabricApiVersions.Version::version) + } fabricApiVersionModel.removeAllElements() fabricApiVersionModel.addAll(apiVersions) fabricApiVersionProperty.set(apiVersions.firstOrNull() ?: emptyVersion) From 22eee065435668218a1fe1917abca677fad00eca Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 31 May 2024 20:17:33 +0200 Subject: [PATCH 038/118] Add property validation support --- .../creator/custom/BuiltinValidations.kt | 44 +++++++++++++++++++ .../creator/custom/TemplateDescriptor.kt | 3 +- .../BuildSystemCoordinatesCreatorProperty.kt | 23 +++++++--- .../custom/types/ClassFqnCreatorProperty.kt | 3 ++ .../custom/types/FabricVersionsProperty.kt | 13 ++++++ .../custom/types/NeoForgeVersionsProperty.kt | 7 +++ .../custom/types/StringCreatorProperty.kt | 15 ++++++- .../messages/MinecraftDevelopment.properties | 1 + 8 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/creator/custom/BuiltinValidations.kt diff --git a/src/main/kotlin/creator/custom/BuiltinValidations.kt b/src/main/kotlin/creator/custom/BuiltinValidations.kt new file mode 100644 index 000000000..b0250c718 --- /dev/null +++ b/src/main/kotlin/creator/custom/BuiltinValidations.kt @@ -0,0 +1,44 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.platform.fabric.util.FabricVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.openapi.ui.ComboBox +import com.intellij.openapi.ui.ValidationInfo +import com.intellij.openapi.ui.validation.DialogValidation +import com.intellij.openapi.ui.validation.validationErrorIf +import com.intellij.openapi.util.text.StringUtil + +object BuiltinValidations { + val validVersion = validationErrorIf(MCDevBundle("creator.validation.semantic_version")) { + SemanticVersion.tryParse(it) == null + } + + val nonEmptyVersion = DialogValidation.WithParameter> { combobox -> + DialogValidation { + if (combobox.item?.parts.isNullOrEmpty()) { + ValidationInfo(MCDevBundle("creator.validation.semantic_version")) + } else { + null + } + } + } + + val nonEmptyYarnVersion = DialogValidation.WithParameter> { combobox -> + DialogValidation { + if (combobox.item == null) { + ValidationInfo(MCDevBundle("creator.validation.semantic_version")) + } else { + null + } + } + } + + val validClassFqn = validationErrorIf(MCDevBundle("creator.validation.class_fqn")) { + it.isBlank() || it.split('.').any { part -> !StringUtil.isJavaIdentifier(part) } + } + + + fun byRegex(regex: Regex): DialogValidation.WithParameter<() -> String> = + validationErrorIf("Must match regex $regex") { !it.matches(regex) } +} diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 804ebe024..8d2cbcdd5 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -27,7 +27,8 @@ data class TemplatePropertyDescriptor( val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, val inheritFrom: String? = null, - val parameters: Map? = null + val parameters: Map? = null, + val validator: Any? = null ) data class PropertyDerivation( diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index 53273d658..bc68eb367 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -1,16 +1,27 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.transform +import com.intellij.openapi.ui.validation.CHECK_ARTIFACT_ID +import com.intellij.openapi.ui.validation.CHECK_GROUP_ID +import com.intellij.openapi.ui.validation.CHECK_NON_EMPTY +import com.intellij.openapi.ui.validation.WHEN_GRAPH_PROPAGATION_FINISHED +import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.ui.dsl.builder.COLUMNS_MEDIUM import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.textValidation + +private val nonExampleValidation = validationErrorIf(MCDevBundle("creator.validation.group_id_non_example")) { + it == "org.example" +} class BuildSystemCoordinatesCreatorProperty( graph: PropertyGraph, @@ -73,22 +84,22 @@ class BuildSystemCoordinatesCreatorProperty( this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.groupIdProperty) .columns(COLUMNS_MEDIUM) -// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) -// .textValidation(CHECK_NON_EMPTY, CHECK_GROUP_ID, nonExampleValidation) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(CHECK_NON_EMPTY, CHECK_GROUP_ID, nonExampleValidation) } this.row(MCDevBundle("creator.ui.group.artifact_id")) { this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.artifactIdProperty) .columns(COLUMNS_MEDIUM) -// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) -// .textValidation(CHECK_NON_EMPTY, CHECK_ARTIFACT_ID) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(CHECK_NON_EMPTY, CHECK_ARTIFACT_ID) } this.row(MCDevBundle("creator.ui.group.version")) { this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.versionProperty) .columns(COLUMNS_MEDIUM) -// .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) -// .textValidation(versionValidation) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(BuiltinValidations.validVersion) } }.expanded = true diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 0f853890e..bcea6c6e7 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates @@ -12,6 +13,7 @@ import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.textValidation class ClassFqnCreatorProperty( graph: PropertyGraph, @@ -29,6 +31,7 @@ class ClassFqnCreatorProperty( panel.row(descriptor.label) { this.textField().bindText(this@ClassFqnCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) + .textValidation(BuiltinValidations.validClassFqn) .enabled(descriptor.editable != false) }.visible(descriptor.hidden != true) } diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index 654b8e1ec..ffa3ccb8b 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -1,6 +1,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.FabricVersionsModel import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions @@ -13,6 +14,7 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindBooleanStorage import com.intellij.openapi.observable.util.not import com.intellij.openapi.observable.util.transform +import com.intellij.openapi.ui.validation.WHEN_GRAPH_PROPAGATION_FINISHED import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.JBColor import com.intellij.ui.dsl.builder.Panel @@ -115,6 +117,9 @@ class FabricVersionsProperty( panel.row("Minecraft Version:") { comboBox(mcVersionModel) .bindItem(mcVersionProperty) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } checkBox("Show snapshots") @@ -124,12 +129,16 @@ class FabricVersionsProperty( panel.row("Loom Version:") { comboBox(loomVersionModel) .bindItem(loomVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) panel.row("Loader Version:") { comboBox(loaderVersionModel) .bindItem(loaderVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) @@ -137,6 +146,8 @@ class FabricVersionsProperty( comboBox(yarnVersionModel) .bindItem(yarnVersionProperty) .enabledIf(useOfficialMappingsProperty.not()) + .validationOnInput(BuiltinValidations.nonEmptyYarnVersion) + .validationOnApply(BuiltinValidations.nonEmptyYarnVersion) .also { ComboboxSpeedSearch.installOn(it.component) } checkBox("Use official mappings") @@ -151,6 +162,8 @@ class FabricVersionsProperty( comboBox(fabricApiVersionModel) .bindItem(fabricApiVersionProperty) .enabledIf(useFabricApiVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } checkBox("Use FabricApi") diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt index 4b0288d2b..e22550f83 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -1,5 +1,6 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions @@ -71,14 +72,20 @@ class NeoForgeVersionsProperty( panel.row(descriptor.label) { comboBox(mcVersionsModel) .bindItem(mcVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } comboBox(nfVersionsModel) .bindItem(nfVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } comboBox(ngVersionsModel) .bindItem(ngVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index ee486c3d0..f8e5a841b 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -1,14 +1,17 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns +import com.intellij.ui.dsl.builder.textValidation class StringCreatorProperty( graph: PropertyGraph, @@ -47,9 +50,19 @@ class StringCreatorProperty( override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.label) { - this.textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) + val textField = textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) + try { + val regexString = descriptor.validator as? String + if (regexString != null) { + val regex = regexString.toRegex() + textField.textValidation(BuiltinValidations.byRegex(regex)) + } + } catch (e: Exception) { + logger() + .error("Failed to create validator for property ${descriptor.name}", e) + } }.visible(descriptor.hidden != true) } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 7830ab791..7641afa3d 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -87,6 +87,7 @@ creator.validation.custom.path_not_a_file=Path is not a file creator.validation.group_id_non_example=Group ID must be changed from "org.example" creator.validation.semantic_version=Version must be a valid semantic version +creator.validation.class_fqn=Must be a valid class fully qualified name creator.validation.jdk_preferred=Java {0} is recommended for {1} creator.validation.jdk_preferred_default_reason=these settings From c3c3edc25514b30be6b0edce4a50b53bad0f81a0 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 15:02:08 +0200 Subject: [PATCH 039/118] Don't even call buildUi if property is hidden --- .../creator/custom/CustomPlatformStep.kt | 4 +++ .../custom/types/NeoForgeVersionsProperty.kt | 34 +++++++++---------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index fa3c7ade8..58c9c31e3 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -236,6 +236,10 @@ class CustomPlatformStep( properties[descriptor.name] = prop + if (descriptor.hidden == true) { + return null + } + val factory = Consumer { panel -> prop.buildUi(panel, context) } val order = descriptor.order ?: 0 return factory to order diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt index e22550f83..10a629f2e 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -88,6 +88,23 @@ class NeoForgeVersionsProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + } + + override fun setupProperty() { + super.setupProperty() + + mcVersionProperty.afterChange { mcVersion -> + if (mcVersion == previousMcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + val availableNfVersions = nfVersion!!.getNeoForgeVersions(mcVersion) + .take(descriptor.limit ?: 50) + nfVersionsModel.removeAllElements() + nfVersionsModel.addAll(availableNfVersions) + nfVersionProperty.set(availableNfVersions.firstOrNull() ?: emptyVersion) + } application.executeOnPooledThread { runBlocking { @@ -129,23 +146,6 @@ class NeoForgeVersionsProperty( } } - override fun setupProperty() { - super.setupProperty() - - mcVersionProperty.afterChange { mcVersion -> - if (mcVersion == previousMcVersion) { - return@afterChange - } - - previousMcVersion = mcVersion - val availableNfVersions = nfVersion!!.getNeoForgeVersions(mcVersion) - .take(descriptor.limit ?: 50) - nfVersionsModel.removeAllElements() - nfVersionsModel.addAll(availableNfVersions) - nfVersionProperty.set(availableNfVersions.firstOrNull() ?: emptyVersion) - } - } - class Factory : CreatorPropertyFactory { override fun create( graph: PropertyGraph, From a2e7e295db4d335db27e3d5e45aba70922c6d55b Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 15:02:31 +0200 Subject: [PATCH 040/118] Add "select" derivation --- .../kotlin/creator/custom/TemplateDescriptor.kt | 10 ++++++++-- .../custom/types/ClassFqnCreatorProperty.kt | 1 + .../creator/custom/types/CreatorProperty.kt | 17 +++++++++++++++++ .../custom/types/IntegerCreatorProperty.kt | 1 + .../types/SemanticVersionCreatorProperty.kt | 1 + .../custom/types/StringCreatorProperty.kt | 1 + 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 8d2cbcdd5..cd2a82c64 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -33,12 +33,18 @@ data class TemplatePropertyDescriptor( data class PropertyDerivation( val parents: List? = null, - val method: String, - val default: Any, + val method: String? = null, + val select: List? = null, + val default: Any? = null, val whenModified: Boolean? = null, val parameters: Map? = null, ) +data class PropertyDerivationSelect( + val condition: String, + val value: Any +) + data class TemplateFile( val template: String, val destination: String, diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index bcea6c6e7..36e1dff05 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -39,6 +39,7 @@ class ClassFqnCreatorProperty( override fun derive(parentValues: List, derivation: PropertyDerivation): ClassFqn { return when (derivation.method) { "suggestClassName" -> suggestClassName(parentValues) + null -> ClassFqn(deriveSelectFirst(parentValues, derivation).toString()) else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 0a57151e2..e7c2bc9f6 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -1,8 +1,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty @@ -52,10 +54,25 @@ abstract class CreatorProperty( * @see GraphProperty.dependsOn */ open fun derive(parentValues: List, derivation: PropertyDerivation): Any? { + if (derivation.select != null) { + return deriveSelectFirst(parentValues, derivation) + } + thisLogger().error("This type doesn't support derivation") return graphProperty.get() } + fun deriveSelectFirst(parentValues: List, derivation: PropertyDerivation): Any? { + val properties = parentValues.mapIndexed { i, value -> derivation.parents!![i] to value }.toMap() + for (select in derivation.select ?: emptyList()) { + if (TemplateEvaluator.condition(properties, select.condition).getOrLogException(thisLogger()) == true) { + return select.value + } + } + + return derivation.default + } + abstract fun buildUi(panel: Panel, context: WizardContext) open fun setupProperty() { diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 9b10aa416..fcb3936c7 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -38,6 +38,7 @@ class IntegerCreatorProperty( return when (derivation.method) { "recommendJavaVersionForMcVersion" -> recommendJavaVersionForMcVersion(parentValues[0]) "recommendJavaVersionForSpongeApiVersion" -> recommendJavaVersionForSpongeApiVersion(parentValues[0]) + null -> (deriveSelectFirst(parentValues, derivation) as Number).toInt() else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 39d2056cc..89d3fc784 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -35,6 +35,7 @@ open class SemanticVersionCreatorProperty( override fun derive(parentValues: List, derivation: PropertyDerivation): Any { return when (derivation.method) { "extractVersionMajorMinor" -> extractVersionMajorMinor(parentValues[0]) + null -> SemanticVersion.parse(deriveSelectFirst(parentValues, derivation).toString()) else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index f8e5a841b..4837fba77 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -30,6 +30,7 @@ class StringCreatorProperty( override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { return when (derivation.method) { "suggestSpongePluginId" -> suggestSpongePluginId(parentValues.first()) + null -> deriveSelectFirst(parentValues, derivation).toString() else -> throw IllegalArgumentException("Unknown method derivation $derivation") } } From fa597e9cc982a95ab6afd1ff3eb51da04bc4a74f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:57:27 +0200 Subject: [PATCH 041/118] Fix templates not getting access to builtin properties --- .../creator/custom/CustomPlatformStep.kt | 31 +++++++++++-------- .../creator/custom/TemplateEvaluator.kt | 10 +++--- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 58c9c31e3..b5742dbac 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -277,25 +277,30 @@ class CustomPlatformStep( val relativeDest = TemplateEvaluator.template(assets.templateProperties, file.destination).getOrNull() ?: continue - val templateContents = template.loadTemplateContents(relativeTemplate) - ?: continue + try { + val templateContents = template.loadTemplateContents(relativeTemplate) + ?: continue + + val destPath = projectPath.resolve(relativeDest).toAbsolutePath() + if (!destPath.startsWith(projectPath)) { + // We want to make sure template files aren't 'escaping' the project directory + continue + } - val destPath = projectPath.resolve(relativeDest).toAbsolutePath() - if (!destPath.startsWith(projectPath)) { - // We want to make sure template files aren't 'escaping' the project directory - continue + val fileName = destPath.fileName.toString().removeSuffix(".ft") + val baseFileName = FileUtilRt.getNameWithoutExtension(fileName) + val extension = FileUtilRt.getExtension(fileName) + val fileTemplate = CustomFileTemplate(baseFileName, extension) + fileTemplate.text = templateContents + assets.addAssets(GeneratorTemplateFile(projectPath.relativize(destPath).toString(), fileTemplate)) + } catch (e: Exception) { + thisLogger().error("Failed to process template file $file", e) } - - val fileName = destPath.fileName.toString().removeSuffix(".ft") - val baseFileName = FileUtilRt.getNameWithoutExtension(fileName) - val extension = FileUtilRt.getExtension(fileName) - val fileTemplate = CustomFileTemplate(baseFileName, extension) - fileTemplate.text = templateContents - assets.addAssets(GeneratorTemplateFile(projectPath.relativize(destPath).toString(), fileTemplate)) } } private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { + into.putAll(TemplateEvaluator.baseProperties) return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } } diff --git a/src/main/kotlin/creator/custom/TemplateEvaluator.kt b/src/main/kotlin/creator/custom/TemplateEvaluator.kt index 58c65bf39..b12d72203 100644 --- a/src/main/kotlin/creator/custom/TemplateEvaluator.kt +++ b/src/main/kotlin/creator/custom/TemplateEvaluator.kt @@ -8,12 +8,12 @@ import org.apache.velocity.util.StringBuilderWriter object TemplateEvaluator { - fun evaluate(properties: Map, template: String): Result> { - val baseProperties = mapOf( - "SemanticVersion" to SemanticVersion, - "MinecraftVersions" to MinecraftVersions - ) + val baseProperties = mapOf( + "semver" to SemanticVersion.Companion, + "mcver" to MinecraftVersions + ) + fun evaluate(properties: Map, template: String): Result> { val context = VelocityContext(baseProperties + properties) val stringWriter = StringBuilderWriter() return runCatching { From 951aaea0b7a6e604232fb18463d508dc239d3cf0 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:58:01 +0200 Subject: [PATCH 042/118] Include license displayname in LicenseData --- src/main/kotlin/creator/custom/model/LicenseData.kt | 1 + src/main/kotlin/creator/custom/types/LicenseProperty.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/model/LicenseData.kt b/src/main/kotlin/creator/custom/model/LicenseData.kt index 286d1865d..043be3e4a 100644 --- a/src/main/kotlin/creator/custom/model/LicenseData.kt +++ b/src/main/kotlin/creator/custom/model/LicenseData.kt @@ -5,5 +5,6 @@ import java.time.ZonedDateTime @TemplateApi data class LicenseData( val id: String, + val name: String, val year: String = ZonedDateTime.now().year.toString(), ) diff --git a/src/main/kotlin/creator/custom/types/LicenseProperty.kt b/src/main/kotlin/creator/custom/types/LicenseProperty.kt index 7c5fdebcb..e0d73d8a1 100644 --- a/src/main/kotlin/creator/custom/types/LicenseProperty.kt +++ b/src/main/kotlin/creator/custom/types/LicenseProperty.kt @@ -28,7 +28,7 @@ class LicenseProperty( override fun serialize(value: LicenseData): String = value.id override fun deserialize(string: String): LicenseData = - LicenseData(string, ZonedDateTime.now().year.toString()) + LicenseData(string, License.byId(string)?.toString() ?: string, ZonedDateTime.now().year.toString()) override fun buildUi(panel: Panel, context: WizardContext) { panel.row(descriptor.label) { From 335a6b6d9ffa3e66e6a0478ec25cb357d75484ef Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:58:14 +0200 Subject: [PATCH 043/118] Add 1.16 & 1.20.6 to MinecraftVersions --- src/main/kotlin/util/MinecraftVersions.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/util/MinecraftVersions.kt b/src/main/kotlin/util/MinecraftVersions.kt index 9d19a575c..939571683 100644 --- a/src/main/kotlin/util/MinecraftVersions.kt +++ b/src/main/kotlin/util/MinecraftVersions.kt @@ -25,6 +25,7 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion object MinecraftVersions { val MC1_12_2 = SemanticVersion.release(1, 12, 2) val MC1_14_4 = SemanticVersion.release(1, 14, 4) + val MC1_16 = SemanticVersion.release(1, 16) val MC1_16_1 = SemanticVersion.release(1, 16, 1) val MC1_16_5 = SemanticVersion.release(1, 16, 5) val MC1_17 = SemanticVersion.release(1, 17) From e4836efcf0bd4e78b3b8d2e6b9ce80bb9e48a4ae Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:58:22 +0200 Subject: [PATCH 044/118] Remove unused class --- .../creator/custom/DerivationMethods.kt | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 src/main/kotlin/creator/custom/DerivationMethods.kt diff --git a/src/main/kotlin/creator/custom/DerivationMethods.kt b/src/main/kotlin/creator/custom/DerivationMethods.kt deleted file mode 100644 index d6d0d0fde..000000000 --- a/src/main/kotlin/creator/custom/DerivationMethods.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.demonwav.mcdev.creator.custom - -import com.demonwav.mcdev.util.SemanticVersion - -typealias DerivationMethod = (from: Any?, properties: Map, parameters: Map) -> Any? - -object DerivationMethods { - - val methods: Map = mapOf( - "extractVersionMajorMinor" to ::extractVersionMajorMinor - ) - - fun extractVersionMajorMinor(from: Any?, properties: Map, parameters: Map): SemanticVersion? { - if (from !is String) { - return null - } - - val version = SemanticVersion.parse(from) - if (version.parts.size < 2) { - return null - } - - val (part1, part2) = version.parts - if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && - part2 is SemanticVersion.Companion.VersionPart.ReleasePart - ) { - return SemanticVersion(listOf(part1, part2)) - } - - return null - } -} From 897baf726ac04a10f8fae79b892e986dbaf31478 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:58:43 +0200 Subject: [PATCH 045/118] Some ClassFqn doc & withClassName --- .../kotlin/creator/custom/model/ClassFqn.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/kotlin/creator/custom/model/ClassFqn.kt b/src/main/kotlin/creator/custom/model/ClassFqn.kt index 54cd457f2..4684d5b82 100644 --- a/src/main/kotlin/creator/custom/model/ClassFqn.kt +++ b/src/main/kotlin/creator/custom/model/ClassFqn.kt @@ -3,10 +3,27 @@ package com.demonwav.mcdev.creator.custom.model @TemplateApi data class ClassFqn(val fqn: String) { + /** + * The [Class.simpleName] of this class. + */ val className by lazy { fqn.substringAfterLast('.') } + + /** + * The relative filesystem path to this class, without extension. + */ val path by lazy { fqn.replace('.', '/') } + + /** + * The package name of this FQN as it would appear in source code. + */ val packageName by lazy { fqn.substringBeforeLast('.') } + + /** + * The package path of this FQN reflected as a local filesystem path + */ val packagePath by lazy { packageName.replace('.', '/') } + fun withClassName(className: String) = copy("$packageName.$className") + override fun toString(): String = fqn } From f7f3894effab4622cf378eca37d30d65cb58bbe9 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 1 Jun 2024 20:59:03 +0200 Subject: [PATCH 046/118] Add ForgeVersions --- .../creator/custom/model/ForgeVersions.kt | 24 +++ .../custom/types/ForgeVersionsProperty.kt | 141 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 3 +- 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/creator/custom/model/ForgeVersions.kt create mode 100644 src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt diff --git a/src/main/kotlin/creator/custom/model/ForgeVersions.kt b/src/main/kotlin/creator/custom/model/ForgeVersions.kt new file mode 100644 index 000000000..899590fa6 --- /dev/null +++ b/src/main/kotlin/creator/custom/model/ForgeVersions.kt @@ -0,0 +1,24 @@ +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.util.SemanticVersion + +@TemplateApi +data class ForgeVersions( + val minecraft: SemanticVersion, + val forge: SemanticVersion, +) : HasMinecraftVersion { + override val minecraftVersion = minecraft + + val minecraftNext by lazy { + val mcNext = when (val part = minecraft.parts.getOrNull(1)) { + // Mimics the code used to get the next Minecraft version in Forge's MDK + // https://github.com/MinecraftForge/MinecraftForge/blob/0ff8a596fc1ef33d4070be89dd5cb4851f93f731/build.gradle#L884 + is SemanticVersion.Companion.VersionPart.ReleasePart -> (part.version + 1).toString() + null -> "?" + else -> part.versionString + } + + "1.$mcNext" + } + val forgeSpec by lazy { forge.parts[0].versionString } +} diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt new file mode 100644 index 000000000..38a680805 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt @@ -0,0 +1,141 @@ +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.custom.BuiltinValidations +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.ForgeVersions +import com.demonwav.mcdev.platform.forge.version.ForgeVersion +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.util.application +import javax.swing.DefaultComboBoxModel +import kotlin.collections.Map +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class ForgeVersionsProperty( + descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val emptyVersion = SemanticVersion.release() + + private val defaultValue = createDefaultValue(descriptor.default) + + override val graphProperty: GraphProperty = graph.property(defaultValue) + var versions: ForgeVersions by graphProperty + + private var forgeVersion: ForgeVersion? = null + private var previousMcVersion: SemanticVersion? = null + + private val mcVersionProperty = graphProperty.transform({ it.minecraft }, { versions.copy(minecraft = it) }) + private val mcVersionsModel = DefaultComboBoxModel() + private val forgeVersionProperty = graphProperty.transform({ it.forge }, { versions.copy(forge = it) }) + private val forgeVersionsModel = DefaultComboBoxModel() + + override fun createDefaultValue(raw: Any?): ForgeVersions { + if (raw is String) { + return deserialize(raw) + } + + return ForgeVersions(emptyVersion, emptyVersion) + } + + override fun serialize(value: ForgeVersions): String { + return "${value.minecraft} ${value.forge}" + } + + override fun deserialize(string: String): ForgeVersions { + val versions = string.split(' ') + .take(2) + .map { SemanticVersion.tryParse(it) ?: emptyVersion } + + return ForgeVersions( + versions.getOrNull(0) ?: emptyVersion, + versions.getOrNull(1) ?: emptyVersion, + ) + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row(descriptor.label) { + comboBox(mcVersionsModel) + .bindItem(mcVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + + comboBox(forgeVersionsModel) + .bindItem(forgeVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + } + + override fun setupProperty() { + super.setupProperty() + + mcVersionProperty.afterChange { mcVersion -> + if (mcVersion == previousMcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + val availableForgeVersions = forgeVersion!!.getForgeVersions(mcVersion) + .take(descriptor.limit ?: 50) + forgeVersionsModel.removeAllElements() + forgeVersionsModel.addAll(availableForgeVersions) + forgeVersionProperty.set(availableForgeVersions.firstOrNull() ?: emptyVersion) + } + + application.executeOnPooledThread { + runBlocking { + val forgeVersions = ForgeVersion.downloadData() + val mcVersions = forgeVersions?.sortedMcVersions?.let { mcVersion -> + val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String + if (filterExpr != null) { + mcVersion.filter { version -> + val conditionProps = mapOf("MC_VERSION" to version) + TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true) + } + } else { + mcVersion + } + } + + if (forgeVersions != null && !mcVersions.isNullOrEmpty()) { + withContext(Dispatchers.Swing) { + forgeVersion = forgeVersions + + mcVersionsModel.removeAllElements() + mcVersionsModel.addAll(mcVersions) + + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraft in mcVersions -> defaultValue.minecraft + else -> mcVersions.first() + } + mcVersionProperty.set(selectedMcVersion) + } + } + } + } + } + + class Factory : CreatorPropertyFactory { + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = ForgeVersionsProperty(descriptor, graph, properties) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index c853cdf3f..f02133018 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -133,12 +133,13 @@ + + - From 79617baa3e3d95b060c1a9db2d11948b60332c8a Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 12:54:26 +0200 Subject: [PATCH 047/118] Allow to get template from outside the template root --- src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index 0716330a3..aa0d5f039 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -23,7 +23,8 @@ class VfsLoadedTemplate( ) : LoadedTemplate { override fun loadTemplateContents(path: String): String? { - val virtualFile = root.fileSystem.findFileByPath(root.path + "/" + path) + root.refresh(false, true) + val virtualFile = root.findFileByRelativePath(path) ?: throw FileNotFoundException("Could not find file $path in template root ${root.path}") virtualFile.refresh(false, false) return virtualFile.readText() From 1a992c08bc67dc896ab53d087ba1a3db5983eb36 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 16:20:02 +0200 Subject: [PATCH 048/118] Builtin templates update --- src/main/kotlin/MinecraftConfigurable.kt | 8 +++ src/main/kotlin/MinecraftSettings.kt | 8 +++ .../creator/custom/CustomPlatformStep.kt | 32 +++++++++++- .../providers/BuiltInTemplateProvider.kt | 51 ++++++++++++++++++- .../custom/providers/TemplateProvider.kt | 3 ++ .../messages/MinecraftDevelopment.properties | 4 ++ 6 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index 815131f7f..5614a4ac5 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -91,6 +91,14 @@ class MinecraftConfigurable : Configurable { } } + group(MCDevBundle("minecraft.settings.creator")) { + row { + checkBox(MCDevBundle("minecraft.settings.creator.auto_update_builtin_templates")) + .comment(MCDevBundle("minecraft.settings.creator.auto_update_builtin_templates.comment")) + .bindSelected(settings::isAutoUpdateBuiltinTemplate) + } + } + onApply { for (project in ProjectManager.getInstance().openProjects) { ProjectView.getInstance(project).refresh() diff --git a/src/main/kotlin/MinecraftSettings.kt b/src/main/kotlin/MinecraftSettings.kt index b4b596114..ff2eb8628 100644 --- a/src/main/kotlin/MinecraftSettings.kt +++ b/src/main/kotlin/MinecraftSettings.kt @@ -37,6 +37,8 @@ class MinecraftSettings : PersistentStateComponent { var underlineType: UnderlineType = UnderlineType.DOTTED, var isShadowAnnotationsSameLine: Boolean = true, + + var autoUpdateBuiltinTemplate: Boolean = true, ) private var state = State() @@ -86,6 +88,12 @@ class MinecraftSettings : PersistentStateComponent { state.isShadowAnnotationsSameLine = shadowAnnotationsSameLine } + var isAutoUpdateBuiltinTemplate: Boolean + get() = state.autoUpdateBuiltinTemplate + set(autoUpdateBuiltinTemplate) { + state.autoUpdateBuiltinTemplate = autoUpdateBuiltinTemplate + } + enum class UnderlineType(private val regular: String, val effectType: EffectType) { NORMAL("Normal", EffectType.LINE_UNDERSCORE), diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index b5742dbac..212a03985 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -128,9 +128,39 @@ class CustomPlatformStep( templatePropertyPlaceholder = placeholder().align(AlignX.FILL) }.topGap(TopGap.SMALL) + initTemplates(null) + templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) } + private fun initTemplates( + taskParentComponent: JComponent? + ) { + selectedTemplate = EmptyLoadedTemplate + + val task = object : Task.Modal( + context.project, + taskParentComponent, + MCDevBundle("creator.step.generic.init_template_providers.message"), + false + ) { + + override fun run(indicator: ProgressIndicator) { + if (project?.isDisposed == true) { + return + } + + for (provider in templateProviders) { + indicator.text = provider.getLabel() + runCatching { provider.init(indicator) } + .getOrLogException(logger()) + } + } + } + + ProgressManager.getInstance().run(task) + } + private fun loadTemplatesInBackground( provider: () -> Collection, taskParentComponent: JComponent? @@ -140,7 +170,7 @@ class CustomPlatformStep( val task = object : Task.WithResult, Exception>( context.project, taskParentComponent, - MCDevBundle("creator.step.generic.project_created.message"), + MCDevBundle("creator.step.generic.init_template_providers.message"), false ) { diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index bc4d303ef..89fd333ba 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -1,23 +1,72 @@ package com.demonwav.mcdev.creator.custom.providers +import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.virtualFile +import com.github.kittinunf.fuel.core.FuelManager +import com.github.kittinunf.result.map +import com.github.kittinunf.result.onError import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.io.ZipUtil import java.util.function.Consumer import javax.swing.JComponent +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.moveTo +import kotlin.io.path.writeBytes class BuiltInTemplateProvider : TemplateProvider { + private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") + private var updatedBuiltinTemplates = false + override fun getLabel(): String = "Built In" + override fun init(indicator: ProgressIndicator) { + if (!updatedBuiltinTemplates && MinecraftSettings.instance.isAutoUpdateBuiltinTemplate) { + indicator.text2 = "Updating builtin templates" + + val manager = FuelManager() + val url = "https://github.com/RedNesto/mcdev-templates/archive/refs/heads/main.zip" + + manager.proxy = selectProxy(url) + + val (_, _, result) = manager.get(url) + .header("User-Agent", "github_org/minecraft-dev/${PluginUtil.pluginVersion}") + .header("Accepts", "application/json") + .timeout(10000) + .response() + + result.onError { + thisLogger().warn("Could not fetch builtin templates update", it) + }.map { data -> + try { + val builtinTemplatesZipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") + builtinTemplatesZipPath.writeBytes(data) + FileUtil.deleteRecursively(builtinTemplatesPath) + ZipUtil.extract(builtinTemplatesZipPath, builtinTemplatesPath, null) + for (child in builtinTemplatesPath.resolve("mcdev-templates-main").listDirectoryEntries()) { + child.moveTo(builtinTemplatesPath.resolve(child.fileName)) + } + updatedBuiltinTemplates = true + thisLogger().info("Builtin template update applied successfully") + } catch (e: Exception) { + thisLogger().error("Failed to apply builtin templates update", e) + } + } + } + } + override fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, provideTemplate: Consumer<() -> Collection> ): JComponent? { provideTemplate.accept { - val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") builtinTemplatesPath.virtualFile?.let(TemplateProvider::findTemplates) ?: emptyList() } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 97d06c9f2..a1a1cc01e 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -8,6 +8,7 @@ import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.readText import java.util.function.Consumer @@ -22,6 +23,8 @@ interface TemplateProvider { fun getTooltip(): String? = null + fun init(indicator: ProgressIndicator) = Unit + fun setupUi( context: WizardContext, propertyGraph: PropertyGraph, diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 7641afa3d..55c3febd6 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -71,6 +71,7 @@ creator.ui.generic_unfinished.message=Haven''t finished {0} creator.ui.create_minecraft_project=Create a new Minecraft project creator.step.generic.project_created.message=Your project is being created +creator.step.generic.init_template_providers.message=Initializing Templates creator.step.gradle.patch_gradle.description=Patching Gradle files creator.step.gradle.import_gradle.description=Importing Gradle project @@ -221,6 +222,9 @@ minecraft.settings.show_chat_color_underlines=Show chat color underlines minecraft.settings.chat_color_underline_style=Chat color underline style: minecraft.settings.mixin=Mixin minecraft.settings.mixin.shadow_annotation_same_line=@Shadow annotations on same line +minecraft.settings.creator=Creator +minecraft.settings.creator.auto_update_builtin_templates=Automatically update Built In templates +minecraft.settings.creator.auto_update_builtin_templates.comment=Updates happen when opening the creator once every IDE session, disable if updating fails consistently for you minecraft.settings.lang_template.display_name=Localization Template minecraft.settings.lang_template.scheme=Scheme: minecraft.settings.lang_template.project_must_be_selected=You must have selected a project for this! From 469fcc4b31fd019bc2eb73316ef65ae83fef988a Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 16:37:31 +0200 Subject: [PATCH 049/118] Add templates repo as resource modules Helps with template completion using velocity implicit hints --- build.gradle.kts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6b8d312e9..1c4116d7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -72,6 +72,26 @@ val gradleToolingExtensionJar = tasks.register(gradleToolingExtensionSource archiveClassifier.set("gradle-tooling-extension") } +val templatesSourceSet: SourceSet = sourceSets.create("templates") { + resources { + srcDir("templates") + compileClasspath += sourceSets.main.get().output + } +} + +val templateSourceSets: List = (file("templates").listFiles() ?: emptyArray()).mapNotNull { file -> + if (file.isDirectory() && (file.listFiles() ?: emptyArray()).any { it.name.endsWith(".mcdev.template.json") }) { + sourceSets.create("templates-${file.name}") { + resources { + srcDir(file) + compileClasspath += sourceSets.main.get().output + } + } + } else { + null + } +} + val externalAnnotationsJar = tasks.register("externalAnnotationsJar") { from("externalAnnotations") destinationDirectory.set(layout.buildDirectory.dir("externalAnnotations")) @@ -381,7 +401,7 @@ tasks.withType { from(externalAnnotationsJar) { into("Minecraft Development/lib/resources") } - from("mcdev-templates") { + from("templates") { into("Minecraft Development/lib/resources/builtin-templates") } } From d6b9ccb5b267b8c317259192336d339e352aead4 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 16:58:31 +0200 Subject: [PATCH 050/118] Flatten a bit the builtin template update code --- .../providers/BuiltInTemplateProvider.kt | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 89fd333ba..f16dcd5b7 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -5,8 +5,10 @@ import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.virtualFile import com.github.kittinunf.fuel.core.FuelManager +import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.map import com.github.kittinunf.result.onError +import com.github.kittinunf.result.success import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph @@ -41,22 +43,23 @@ class BuiltInTemplateProvider : TemplateProvider { .timeout(10000) .response() - result.onError { + val data = result.onError { thisLogger().warn("Could not fetch builtin templates update", it) - }.map { data -> - try { - val builtinTemplatesZipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") - builtinTemplatesZipPath.writeBytes(data) - FileUtil.deleteRecursively(builtinTemplatesPath) - ZipUtil.extract(builtinTemplatesZipPath, builtinTemplatesPath, null) - for (child in builtinTemplatesPath.resolve("mcdev-templates-main").listDirectoryEntries()) { - child.moveTo(builtinTemplatesPath.resolve(child.fileName)) - } - updatedBuiltinTemplates = true - thisLogger().info("Builtin template update applied successfully") - } catch (e: Exception) { - thisLogger().error("Failed to apply builtin templates update", e) + }.getOrNull() ?: return + + try { + val builtinTemplatesZipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") + builtinTemplatesZipPath.writeBytes(data) + FileUtil.deleteRecursively(builtinTemplatesPath) + ZipUtil.extract(builtinTemplatesZipPath, builtinTemplatesPath, null) + for (child in builtinTemplatesPath.resolve("mcdev-templates-main").listDirectoryEntries()) { + child.moveTo(builtinTemplatesPath.resolve(child.fileName)) } + + updatedBuiltinTemplates = true + thisLogger().info("Builtin template update applied successfully") + } catch (e: Exception) { + thisLogger().error("Failed to apply builtin templates update", e) } } } From 693a691cb6b7eb73f8d419e5012401eb73c76487 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 17:05:18 +0200 Subject: [PATCH 051/118] Ktlint fixes --- .../creator/ProjectSetupFinalizerWizardStep.kt | 1 - .../kotlin/creator/custom/BuiltinValidations.kt | 1 - .../creator/custom/model/FabricVersionsModel.kt | 1 - .../custom/providers/BuiltInTemplateProvider.kt | 8 +++----- .../custom/providers/LocalTemplateProvider.kt | 14 ++++++++------ .../creator/custom/providers/TemplateProvider.kt | 4 +++- .../custom/providers/ZipTemplateProvider.kt | 9 +++++---- .../types/BuildSystemCoordinatesCreatorProperty.kt | 13 ++++++------- .../kotlin/creator/custom/types/CreatorProperty.kt | 8 ++++++-- .../creator/custom/types/CreatorPropertyFactory.kt | 9 +++++---- .../custom/types/ExternalCreatorProperty.kt | 9 ++++++--- .../creator/custom/types/FabricVersionsProperty.kt | 7 +++++-- .../creator/custom/types/IntegerCreatorProperty.kt | 1 - .../creator/custom/types/JdkCreatorProperty.kt | 5 ++++- .../creator/custom/types/ParchmentProperty.kt | 3 ++- 15 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt b/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt index 77c5a9740..6c3b70902 100644 --- a/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt +++ b/src/main/kotlin/creator/ProjectSetupFinalizerWizardStep.kt @@ -28,7 +28,6 @@ import com.intellij.ide.wizard.AbstractNewProjectWizardStep import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.ObservableProperty import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdkVersion diff --git a/src/main/kotlin/creator/custom/BuiltinValidations.kt b/src/main/kotlin/creator/custom/BuiltinValidations.kt index b0250c718..1b87ee055 100644 --- a/src/main/kotlin/creator/custom/BuiltinValidations.kt +++ b/src/main/kotlin/creator/custom/BuiltinValidations.kt @@ -38,7 +38,6 @@ object BuiltinValidations { it.isBlank() || it.split('.').any { part -> !StringUtil.isJavaIdentifier(part) } } - fun byRegex(regex: Regex): DialogValidation.WithParameter<() -> String> = validationErrorIf("Must match regex $regex") { !it.matches(regex) } } diff --git a/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt index 06f82d706..0b9bf98f1 100644 --- a/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt +++ b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt @@ -1,6 +1,5 @@ package com.demonwav.mcdev.creator.custom.model -import com.demonwav.mcdev.platform.fabric.creator.FabricMcVersion import com.demonwav.mcdev.platform.fabric.util.FabricVersions import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index f16dcd5b7..26c10a8ea 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -6,9 +6,7 @@ import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.virtualFile import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull -import com.github.kittinunf.result.map import com.github.kittinunf.result.onError -import com.github.kittinunf.result.success import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph @@ -48,10 +46,10 @@ class BuiltInTemplateProvider : TemplateProvider { }.getOrNull() ?: return try { - val builtinTemplatesZipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") - builtinTemplatesZipPath.writeBytes(data) + val zipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") + zipPath.writeBytes(data) FileUtil.deleteRecursively(builtinTemplatesPath) - ZipUtil.extract(builtinTemplatesZipPath, builtinTemplatesPath, null) + ZipUtil.extract(zipPath, builtinTemplatesPath, null) for (child in builtinTemplatesPath.resolve("mcdev-templates-main").listDirectoryEntries()) { child.moveTo(builtinTemplatesPath.resolve(child.fileName)) } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index 5f5615ef7..f63aff744 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -50,12 +50,14 @@ class LocalTemplateProvider : TemplateProvider { ).align(AlignX.FILL) .columns(COLUMNS_LARGE) .bindText(pathProperty) - .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_directory")) { value -> - val file = kotlin.runCatching { - VirtualFileManager.getInstance().findFileByNioPath(Path.of(value)) - }.getOrNull() - file == null || !file.isDirectory - }) + .textValidation( + validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_directory")) { value -> + val file = kotlin.runCatching { + VirtualFileManager.getInstance().findFileByNioPath(Path.of(value)) + }.getOrNull() + file == null || !file.isDirectory + } + ) } } } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index a1a1cc01e..970eb35b4 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -98,7 +98,9 @@ interface TemplateProvider { val mergedFiles = parentDescriptor.files + descriptor.files descriptor = descriptor.copy(properties = mergedProperties, files = mergedFiles) } else { - thisLogger().error("Could not find inherited template descriptor ${descriptor.inherit} from ${descriptorFile.path}") + thisLogger().error( + "Could not find inherited template descriptor ${descriptor.inherit} from ${descriptorFile.path}" + ) } } diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index b25703e15..8a762062c 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -1,7 +1,6 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle -import com.intellij.ide.highlighter.ArchiveFileType import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph @@ -49,9 +48,11 @@ class ZipTemplateProvider : TemplateProvider { ).align(AlignX.FILL) .columns(COLUMNS_LARGE) .bindText(pathProperty) - .textValidation(validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_file")) { value -> - runCatching { !Path.of(value).isRegularFile() }.getOrDefault(true) - }) + .textValidation( + validationErrorIf(MCDevBundle("creator.validation.custom.path_not_a_file")) { value -> + runCatching { !Path.of(value).isRegularFile() }.getOrDefault(true) + } + ) } } } diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index bc68eb367..cc0a1436f 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -84,25 +84,24 @@ class BuildSystemCoordinatesCreatorProperty( this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.groupIdProperty) .columns(COLUMNS_MEDIUM) - .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) - .textValidation(CHECK_NON_EMPTY, CHECK_GROUP_ID, nonExampleValidation) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(CHECK_NON_EMPTY, CHECK_GROUP_ID, nonExampleValidation) } this.row(MCDevBundle("creator.ui.group.artifact_id")) { this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.artifactIdProperty) .columns(COLUMNS_MEDIUM) - .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) - .textValidation(CHECK_NON_EMPTY, CHECK_ARTIFACT_ID) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(CHECK_NON_EMPTY, CHECK_ARTIFACT_ID) } this.row(MCDevBundle("creator.ui.group.version")) { this.textField() .bindText(this@BuildSystemCoordinatesCreatorProperty.versionProperty) .columns(COLUMNS_MEDIUM) - .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) - .textValidation(BuiltinValidations.validVersion) + .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) + .textValidation(BuiltinValidations.validVersion) } }.expanded = true - } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index e7c2bc9f6..8b0f94f3e 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -85,7 +85,9 @@ abstract class CreatorProperty( ?: throw RuntimeException("No parents specified in derivation of property '${descriptor.name}'") for (parent in parents) { if (!properties.containsKey(parent)) { - throw RuntimeException("Unknown parent property '${parent}' in derivation of property '${descriptor.name}'") + throw RuntimeException( + "Unknown parent property '$parent' in derivation of property '${descriptor.name}'" + ) } } @@ -104,7 +106,9 @@ abstract class CreatorProperty( if (descriptor.inheritFrom != null) { val parentProperty = properties[descriptor.inheritFrom] - ?: throw RuntimeException("Unknown parent property '${descriptor.inheritFrom}' in derivation of property '${descriptor.name}'") + ?: throw RuntimeException( + "Unknown parent property '${descriptor.inheritFrom}' in derivation of property '${descriptor.name}'" + ) @Suppress("UNCHECKED_CAST") graphProperty.set(parentProperty.graphProperty.get() as T) diff --git a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt index b47c66b80..1b512d272 100644 --- a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt +++ b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt @@ -13,8 +13,9 @@ interface CreatorPropertyFactory { companion object { - private val EP_NAME = - ExtensionPointName>("com.demonwav.minecraft-dev.creatorPropertyType") + private val EP_NAME = ExtensionPointName>( + "com.demonwav.minecraft-dev.creatorPropertyType" + ) private val COLLECTOR = KeyedExtensionCollector(EP_NAME) @@ -35,8 +36,8 @@ interface CreatorPropertyFactory { ): CreatorProperty<*> } -class CreatorPropertyFactoryBean : BaseKeyedLazyInstance(), - KeyedLazyInstance { +class CreatorPropertyFactoryBean : + BaseKeyedLazyInstance(), KeyedLazyInstance { @Attribute("type") @RequiredElement diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index e8f060660..d4a434afa 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -15,11 +15,14 @@ class ExternalCreatorProperty( override fun setupProperty() = Unit - override fun createDefaultValue(raw: Any?): T = throw UnsupportedOperationException("Unsupported for external properties") + override fun createDefaultValue(raw: Any?): T = + throw UnsupportedOperationException("Unsupported for external properties") - override fun serialize(value: T): String = throw UnsupportedOperationException("Unsupported for external properties") + override fun serialize(value: T): String = + throw UnsupportedOperationException("Unsupported for external properties") - override fun deserialize(string: String): T = throw UnsupportedOperationException("Unsupported for external properties") + override fun deserialize(string: String): T = + throw UnsupportedOperationException("Unsupported for external properties") override fun buildUi(panel: Panel, context: WizardContext) = Unit } diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index ffa3ccb8b..7eacb9f0e 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -91,7 +91,8 @@ class FabricVersionsProperty( } override fun serialize(value: FabricVersionsModel): String { - return "${value.minecraftVersion} ${value.loom} ${value.loader} ${value.yarn} ${value.useFabricApi} ${value.fabricApi} ${value.useOfficialMappings}" + return "${value.minecraftVersion} ${value.loom} ${value.loader} ${value.yarn}" + + " ${value.useFabricApi} ${value.fabricApi} ${value.useOfficialMappings}" } override fun deserialize(string: String): FabricVersionsModel { @@ -193,7 +194,9 @@ class FabricVersionsProperty( application.executeOnPooledThread { runBlocking { val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } - val loomVersionsJob = asyncIO { collectMavenVersions("https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml") } + val loomVersionsJob = asyncIO { + collectMavenVersions("https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml") + } val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } this@FabricVersionsProperty.fabricVersions = fabricVersionsJob.await() diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index fcb3936c7..7590ebba7 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -3,7 +3,6 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion -import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions import com.demonwav.mcdev.platform.sponge.util.SpongeVersions import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index 41da4b977..8960720ce 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -34,7 +34,10 @@ class JdkCreatorProperty( val minVersionPropName = descriptor.default as? String if (minVersionPropName != null) { val minVersionProperty = properties[minVersionPropName] - ?: throw RuntimeException("Could not find property $minVersionPropName referenced by default value of property ${descriptor.name}") + ?: throw RuntimeException( + "Could not find property $minVersionPropName referenced" + + " by default value of property ${descriptor.name}" + ) jdkComboBox.setPreferredJdk(JavaSdkVersion.entries[minVersionProperty.graphProperty.get() as Int]) minVersionProperty.graphProperty.afterPropagation { diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index 31f4f77df..faf1daa7d 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -56,7 +56,8 @@ class ParchmentProperty( } override fun serialize(value: ParchmentVersions): String { - return "${value.use} ${value.version} ${value.minecraftVersion} ${value.includeOlderMcVersions} ${value.includeSnapshots}" + return "${value.use} ${value.version} ${value.minecraftVersion}" + + " ${value.includeOlderMcVersions} ${value.includeSnapshots}" } override fun deserialize(string: String): ParchmentVersions { From f539be041ecaaf06efcf8dc459f78ce055c335e7 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 17:06:39 +0200 Subject: [PATCH 052/118] Add licenses --- .../creator/custom/BuiltinValidations.kt | 20 +++++++++++++++++++ .../EvaluateTemplateExpressionAction.kt | 20 +++++++++++++++++++ .../creator/custom/RecentProjectTemplates.kt | 20 +++++++++++++++++++ .../creator/custom/TemplateDescriptor.kt | 20 +++++++++++++++++++ .../creator/custom/TemplateEvaluator.kt | 20 +++++++++++++++++++ .../custom/model/BuildSystemCoordinates.kt | 20 +++++++++++++++++++ .../kotlin/creator/custom/model/ClassFqn.kt | 20 +++++++++++++++++++ .../kotlin/creator/custom/model/CreatorJdk.kt | 20 +++++++++++++++++++ .../custom/model/FabricVersionsModel.kt | 20 +++++++++++++++++++ .../creator/custom/model/ForgeVersions.kt | 20 +++++++++++++++++++ .../custom/model/HasMinecraftVersion.kt | 20 +++++++++++++++++++ .../creator/custom/model/LicenseData.kt | 20 +++++++++++++++++++ .../creator/custom/model/NeoForgeVersions.kt | 20 +++++++++++++++++++ .../creator/custom/model/ParchmentVersions.kt | 20 +++++++++++++++++++ .../kotlin/creator/custom/model/StringList.kt | 20 +++++++++++++++++++ .../creator/custom/model/TemplateApi.kt | 20 +++++++++++++++++++ .../providers/BuiltInTemplateProvider.kt | 20 +++++++++++++++++++ .../custom/providers/EmptyLoadedTemplate.kt | 20 +++++++++++++++++++ .../custom/providers/LoadedTemplate.kt | 20 +++++++++++++++++++ .../custom/providers/LocalTemplateProvider.kt | 20 +++++++++++++++++++ .../providers/RecentTemplatesProvider.kt | 20 +++++++++++++++++++ .../custom/providers/TemplateProvider.kt | 20 +++++++++++++++++++ .../custom/providers/VfsLoadedTemplate.kt | 20 +++++++++++++++++++ .../custom/providers/ZipTemplateProvider.kt | 20 +++++++++++++++++++ .../custom/types/BooleanCreatorProperty.kt | 20 +++++++++++++++++++ .../BuildSystemCoordinatesCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/ClassFqnCreatorProperty.kt | 20 +++++++++++++++++++ .../creator/custom/types/CreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/CreatorPropertyFactory.kt | 20 +++++++++++++++++++ .../custom/types/ExternalCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/FabricVersionsProperty.kt | 20 +++++++++++++++++++ .../custom/types/ForgeVersionsProperty.kt | 20 +++++++++++++++++++ .../types/InlineStringListCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/IntegerCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/JdkCreatorProperty.kt | 20 +++++++++++++++++++ .../creator/custom/types/LicenseProperty.kt | 20 +++++++++++++++++++ .../types/MavenArtifactVersionProperty.kt | 20 +++++++++++++++++++ .../custom/types/NeoForgeVersionsProperty.kt | 20 +++++++++++++++++++ .../creator/custom/types/ParchmentProperty.kt | 20 +++++++++++++++++++ .../types/SemanticVersionCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/SimpleCreatorProperty.kt | 20 +++++++++++++++++++ .../custom/types/StringCreatorProperty.kt | 20 +++++++++++++++++++ .../platform/sponge/util/SpongeVersions.kt | 20 +++++++++++++++++++ 43 files changed, 860 insertions(+) diff --git a/src/main/kotlin/creator/custom/BuiltinValidations.kt b/src/main/kotlin/creator/custom/BuiltinValidations.kt index 1b87ee055..8d2ebca56 100644 --- a/src/main/kotlin/creator/custom/BuiltinValidations.kt +++ b/src/main/kotlin/creator/custom/BuiltinValidations.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.asset.MCDevBundle diff --git a/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt b/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt index 4ea8b0a92..d2fc2c771 100644 --- a/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt +++ b/src/main/kotlin/creator/custom/EvaluateTemplateExpressionAction.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.model.ClassFqn diff --git a/src/main/kotlin/creator/custom/RecentProjectTemplates.kt b/src/main/kotlin/creator/custom/RecentProjectTemplates.kt index 7e942bfc2..6ff54deef 100644 --- a/src/main/kotlin/creator/custom/RecentProjectTemplates.kt +++ b/src/main/kotlin/creator/custom/RecentProjectTemplates.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index cd2a82c64..9aa7cb24c 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom data class TemplateDescriptor( diff --git a/src/main/kotlin/creator/custom/TemplateEvaluator.kt b/src/main/kotlin/creator/custom/TemplateEvaluator.kt index b12d72203..b717fe712 100644 --- a/src/main/kotlin/creator/custom/TemplateEvaluator.kt +++ b/src/main/kotlin/creator/custom/TemplateEvaluator.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.util.MinecraftVersions diff --git a/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt index 2854cebf6..0eeab3c6e 100644 --- a/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt +++ b/src/main/kotlin/creator/custom/model/BuildSystemCoordinates.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model @TemplateApi diff --git a/src/main/kotlin/creator/custom/model/ClassFqn.kt b/src/main/kotlin/creator/custom/model/ClassFqn.kt index 4684d5b82..783948836 100644 --- a/src/main/kotlin/creator/custom/model/ClassFqn.kt +++ b/src/main/kotlin/creator/custom/model/ClassFqn.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model @TemplateApi diff --git a/src/main/kotlin/creator/custom/model/CreatorJdk.kt b/src/main/kotlin/creator/custom/model/CreatorJdk.kt index 06278c956..1e442b19b 100644 --- a/src/main/kotlin/creator/custom/model/CreatorJdk.kt +++ b/src/main/kotlin/creator/custom/model/CreatorJdk.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.intellij.openapi.projectRoots.JavaSdk diff --git a/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt index 0b9bf98f1..c5111c7c5 100644 --- a/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt +++ b/src/main/kotlin/creator/custom/model/FabricVersionsModel.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.platform.fabric.util.FabricVersions diff --git a/src/main/kotlin/creator/custom/model/ForgeVersions.kt b/src/main/kotlin/creator/custom/model/ForgeVersions.kt index 899590fa6..a308f4787 100644 --- a/src/main/kotlin/creator/custom/model/ForgeVersions.kt +++ b/src/main/kotlin/creator/custom/model/ForgeVersions.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt index 4f3c82a4c..c33cd9676 100644 --- a/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt +++ b/src/main/kotlin/creator/custom/model/HasMinecraftVersion.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/model/LicenseData.kt b/src/main/kotlin/creator/custom/model/LicenseData.kt index 043be3e4a..ddbf7932c 100644 --- a/src/main/kotlin/creator/custom/model/LicenseData.kt +++ b/src/main/kotlin/creator/custom/model/LicenseData.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import java.time.ZonedDateTime diff --git a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt index a505bc183..add389b79 100644 --- a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt +++ b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/model/ParchmentVersions.kt b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt index 4bde631e0..0d11a3c74 100644 --- a/src/main/kotlin/creator/custom/model/ParchmentVersions.kt +++ b/src/main/kotlin/creator/custom/model/ParchmentVersions.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model import com.demonwav.mcdev.util.SemanticVersion diff --git a/src/main/kotlin/creator/custom/model/StringList.kt b/src/main/kotlin/creator/custom/model/StringList.kt index ea62731c5..d2b3bf09c 100644 --- a/src/main/kotlin/creator/custom/model/StringList.kt +++ b/src/main/kotlin/creator/custom/model/StringList.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model @TemplateApi diff --git a/src/main/kotlin/creator/custom/model/TemplateApi.kt b/src/main/kotlin/creator/custom/model/TemplateApi.kt index 98dd00d74..88c4542cb 100644 --- a/src/main/kotlin/creator/custom/model/TemplateApi.kt +++ b/src/main/kotlin/creator/custom/model/TemplateApi.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.model /** diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 26c10a8ea..0dccf5626 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings diff --git a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt index c6671bb9b..71db87576 100644 --- a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor diff --git a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt index b85b73949..14fa8157d 100644 --- a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index f63aff744..3c75151aa 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle diff --git a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt index a1bbd94e5..2ac5897c4 100644 --- a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.RecentProjectTemplates diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 970eb35b4..c59c3c984 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index aa0d5f039..20be38eb1 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 8a762062c..5cb9c9e5d 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index 219ca9c2d..5229e4556 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index cc0a1436f..0a8d2708f 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.asset.MCDevBundle diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 36e1dff05..0657e723f 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 8b0f94f3e..5735b98fb 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation diff --git a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt index 1b512d272..b75568ac9 100644 --- a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt +++ b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index d4a434afa..999a0770e 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index 7eacb9f0e..3da0a4938 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.collectMavenVersions diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt index 38a680805..2129202f9 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt index e063eedc4..f0ef1a122 100644 --- a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 7590ebba7..a272ac253 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index 8960720ce..f7772bd14 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.JdkComboBoxWithPreference diff --git a/src/main/kotlin/creator/custom/types/LicenseProperty.kt b/src/main/kotlin/creator/custom/types/LicenseProperty.kt index e0d73d8a1..85b516601 100644 --- a/src/main/kotlin/creator/custom/types/LicenseProperty.kt +++ b/src/main/kotlin/creator/custom/types/LicenseProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt index 0aca9f114..89cd90368 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.collectMavenVersions diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt index 10a629f2e..7a8e64da6 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index faf1daa7d..1e7ac3914 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.ParchmentVersion diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 89d3fc784..207ca502f 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 107c0ab41..1a06df0d0 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 4837fba77..56fc06f26 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations diff --git a/src/main/kotlin/platform/sponge/util/SpongeVersions.kt b/src/main/kotlin/platform/sponge/util/SpongeVersions.kt index 640c901d8..8e699d7c7 100644 --- a/src/main/kotlin/platform/sponge/util/SpongeVersions.kt +++ b/src/main/kotlin/platform/sponge/util/SpongeVersions.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.platform.sponge.util import com.demonwav.mcdev.util.SemanticVersion From 59dcc8630d7d52b335ae688b934ce29671820548 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 17:10:46 +0200 Subject: [PATCH 053/118] Revert unneeded change --- src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt index 4e7ff3e37..51614b1fb 100644 --- a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt +++ b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt @@ -37,7 +37,7 @@ abstract class AbstractBuildSystemStep( parent: NewProjectWizardStep, ) : AbstractNewProjectWizardMultiStep(parent, EP_NAME) { companion object { - val PLATFORM_NAME_KEY = Key.create("mcdev.platformName") + private val PLATFORM_NAME_KEY = Key.create("mcdev.platformName") val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.buildSystemWizard") } From 25d904a1bcf0958587459bf29f8cff7b7984d085 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 18:38:05 +0200 Subject: [PATCH 054/118] Make properties & files properly optional Also log when a template cannot be loaded because of and unhandled version --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 4 ++-- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 4 ++-- .../kotlin/creator/custom/providers/TemplateProvider.kt | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 212a03985..1accbc694 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -212,7 +212,7 @@ class CustomPlatformStep( private fun setupTemplate(template: LoadedTemplate): List> { return try { - template.descriptor.properties + template.descriptor.properties.orEmpty() .mapNotNull { setupProperty(it) } .sortedBy { (_, order) -> order } .map { it.first } @@ -295,7 +295,7 @@ class CustomPlatformStep( ?: return thisLogger().error("Could not find wizard base data") val projectPath = Path.of(baseData.path) - for (file in descriptor.files) { + for (file in descriptor.files.orEmpty()) { if (file.condition != null && !TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } ) { diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 9aa7cb24c..7f3d92b5f 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -25,8 +25,8 @@ data class TemplateDescriptor( val label: String? = null, val inherit: String? = null, val hidden: Boolean? = null, - val properties: List, - val files: List, + val properties: List? = null, + val files: List? = null, ) data class TemplatePropertyDescriptor( diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index c59c3c984..46f80006d 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -98,6 +98,7 @@ interface TemplateProvider { var descriptor = Gson().fromJson(descriptorFile.readText()) if (descriptor.version != 1) { + thisLogger().warn("Cannot handle template ${descriptorFile.path} of version ${descriptor.version}") return null } @@ -110,12 +111,12 @@ interface TemplateProvider { ?: root.presentableName if (descriptor.inherit != null) { - val parent = root.findFileByRelativePath(descriptor.inherit!!) + val parent = root.findFileByRelativePath(descriptor.inherit) if (parent != null) { parent.refresh(false, false) val parentDescriptor = Gson().fromJson(parent.readText()) - val mergedProperties = parentDescriptor.properties + descriptor.properties - val mergedFiles = parentDescriptor.files + descriptor.files + val mergedProperties = parentDescriptor.properties.orEmpty() + descriptor.properties.orEmpty() + val mergedFiles = parentDescriptor.files.orEmpty() + descriptor.files.orEmpty() descriptor = descriptor.copy(properties = mergedProperties, files = mergedFiles) } else { thisLogger().error( From f7e49a7e84edcc8d920d29f54a76255df6d324ee Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 19:22:41 +0200 Subject: [PATCH 055/118] Restore required nonnull assert --- src/main/kotlin/creator/custom/providers/TemplateProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 46f80006d..51bc6a7c6 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -111,7 +111,7 @@ interface TemplateProvider { ?: root.presentableName if (descriptor.inherit != null) { - val parent = root.findFileByRelativePath(descriptor.inherit) + val parent = root.findFileByRelativePath(descriptor.inherit!!) if (parent != null) { parent.refresh(false, false) val parentDescriptor = Gson().fromJson(parent.readText()) From 39f3795f2e5adca89bbc247ad98ae29608fbd058 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 2 Jun 2024 20:51:26 +0200 Subject: [PATCH 056/118] Run gradle wrapper task after project import --- .../creator/custom/CustomPlatformStep.kt | 29 +++++++ .../creator/custom/TemplateDescriptor.kt | 1 + .../custom/finalizers/CreatorFinalizer.kt | 86 +++++++++++++++++++ .../finalizers/RunGradleTasksFinalizer.kt | 38 ++++++++ src/main/resources/META-INF/plugin.xml | 5 ++ 5 files changed, 159 insertions(+) create mode 100644 src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt create mode 100644 src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 1accbc694..19af2408b 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.finalizers.CreatorFinalizer import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate import com.demonwav.mcdev.creator.custom.providers.RecentTemplatesProvider @@ -36,6 +37,10 @@ import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType +import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator @@ -49,6 +54,7 @@ import com.intellij.ui.dsl.builder.Placeholder import com.intellij.ui.dsl.builder.SegmentedButton import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel +import com.intellij.util.application import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent @@ -333,4 +339,27 @@ class CustomPlatformStep( into.putAll(TemplateEvaluator.baseProperties) return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } + + override fun perform(project: Project) { + super.perform(project) + + val finalizers = selectedTemplate.descriptor.finalizers + if (finalizers.isNullOrEmpty()) { + return + } + + val projectId = ExternalSystemTaskId.getProjectId(project) + val listener = object : ExternalSystemTaskNotificationListenerAdapter() { + override fun onSuccess(id: ExternalSystemTaskId) { + if (id.type == ExternalSystemTaskType.RESOLVE_PROJECT && projectId == id.ideProjectId) { + application.executeOnPooledThread { + // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that + CreatorFinalizer.executeAll(project, finalizers, assets.templateProperties) + } + ExternalSystemProgressNotificationManager.getInstance().removeNotificationListener(this) + } + } + } + ExternalSystemProgressNotificationManager.getInstance().addNotificationListener(listener) + } } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 7f3d92b5f..c7b7dc1a1 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -27,6 +27,7 @@ data class TemplateDescriptor( val hidden: Boolean? = null, val properties: List? = null, val files: List? = null, + val finalizers: List>? = null, ) data class TemplatePropertyDescriptor( diff --git a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt new file mode 100644 index 000000000..c05551d9c --- /dev/null +++ b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt @@ -0,0 +1,86 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.finalizers + +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.RequiredElement +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.KeyedExtensionCollector +import com.intellij.serviceContainer.BaseKeyedLazyInstance +import com.intellij.util.KeyedLazyInstance +import com.intellij.util.xmlb.annotations.Attribute + +interface CreatorFinalizer { + + fun execute(project: Project, properties: Map, templateProperties: Map) + + companion object { + private val EP_NAME = + ExtensionPointName.create("com.demonwav.minecraft-dev.creatorFinalizer") + private val COLLECTOR = KeyedExtensionCollector(EP_NAME) + + fun executeAll(project: Project, finalizers: List>, templateProperties: Map) { + for (properties in finalizers) { + val type = properties["type"] as? String + if (type == null) { + thisLogger().warn("Missing finalizer 'type' value") + continue + } + + val condition = properties["condition"] as? String + if (condition != null && + !TemplateEvaluator.condition(templateProperties, condition).getOrElse { false } + ) { + continue + } + + val finalizer = COLLECTOR.findSingle(type) + if (finalizer == null) { + thisLogger().warn("Unknown finalizer $type") + continue + } + + try { + finalizer.execute(project, properties, templateProperties) + } catch (e: Exception) { + thisLogger().error("Unhandled exception in finalizer $type", e) + } + } + } + } +} + +class CreatorFinalizerBean : BaseKeyedLazyInstance(), KeyedLazyInstance { + + @Attribute("type") + @RequiredElement + lateinit var type: String + + @Attribute("implementation") + @RequiredElement + lateinit var implementation: String + + override fun getKey(): String? = type + + override fun getImplementationClassName(): String? = implementation +} diff --git a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt new file mode 100644 index 000000000..22bbd4cc3 --- /dev/null +++ b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt @@ -0,0 +1,38 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.finalizers + +import com.demonwav.mcdev.util.runGradleTaskAndWait +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir + +class RunGradleTasksFinalizer : CreatorFinalizer { + + override fun execute(project: Project, properties: Map, templateProperties: Map) { + @Suppress("UNCHECKED_CAST") + val tasks = properties["tasks"] as? List + ?: return + + runGradleTaskAndWait(project, project.guessProjectDir()!!.toNioPath()) { settings -> + settings.taskNames = tasks + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f02133018..f2555a7e0 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -78,6 +78,9 @@ + + + @@ -149,6 +152,8 @@ + + From 9162a77285389ad44e26ada30de242505917c08f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 00:38:26 +0200 Subject: [PATCH 057/118] Add .gitignore and git add generated files after gradle wrapper task --- .../creator/custom/CustomPlatformStep.kt | 5 +++ .../custom/finalizers/GitAddAllFinalizer.kt | 32 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + 3 files changed, 38 insertions(+) create mode 100644 src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 19af2408b..9ebab8257 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -32,6 +32,7 @@ import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep import com.intellij.ide.fileTemplates.impl.CustomFileTemplate import com.intellij.ide.starters.local.GeneratorTemplateFile +import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.getOrLogException @@ -337,6 +338,10 @@ class CustomPlatformStep( private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { into.putAll(TemplateEvaluator.baseProperties) + + val gitData = data.getUserData(GitNewProjectWizardData.KEY) + into["USE_GIT"] = gitData?.git == true + return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } diff --git a/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt new file mode 100644 index 000000000..28ff7316f --- /dev/null +++ b/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt @@ -0,0 +1,32 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.finalizers + +import com.intellij.execution.configurations.GeneralCommandLine +import com.intellij.execution.util.ExecUtil +import com.intellij.openapi.project.Project + +class GitAddAllFinalizer : CreatorFinalizer { + + override fun execute(project: Project, properties: Map, templateProperties: Map) { + ExecUtil.execAndGetOutput(GeneralCommandLine("git", "add", ".").withWorkDirectory(project.basePath)) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f2555a7e0..91fbadea6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -153,6 +153,7 @@ + From e67ee3c0a868c1f054a26773ab8f210c4248ac77 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 17:56:40 +0200 Subject: [PATCH 058/118] Architectury template --- .../custom/model/ArchitecturyVersionsModel.kt | 60 +++ .../kotlin/creator/custom/model/ClassFqn.kt | 2 + .../types/ArchitecturyVersionsProperty.kt | 450 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + 4 files changed, 513 insertions(+) create mode 100644 src/main/kotlin/creator/custom/model/ArchitecturyVersionsModel.kt create mode 100644 src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt diff --git a/src/main/kotlin/creator/custom/model/ArchitecturyVersionsModel.kt b/src/main/kotlin/creator/custom/model/ArchitecturyVersionsModel.kt new file mode 100644 index 000000000..d77d9e22e --- /dev/null +++ b/src/main/kotlin/creator/custom/model/ArchitecturyVersionsModel.kt @@ -0,0 +1,60 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.model + +import com.demonwav.mcdev.platform.fabric.util.FabricVersions +import com.demonwav.mcdev.util.SemanticVersion + +@TemplateApi +data class ArchitecturyVersionsModel( + val minecraft: SemanticVersion, + val forge: SemanticVersion?, + val neoforge: SemanticVersion?, + val loom: SemanticVersion, + val loader: SemanticVersion, + val yarn: FabricVersions.YarnVersion, + val useFabricApi: Boolean, + val fabricApi: SemanticVersion, + val useOfficialMappings: Boolean, + val useArchitecturyApi: Boolean, + val architecturyApi: SemanticVersion, +) : HasMinecraftVersion { + + override val minecraftVersion: SemanticVersion = minecraft + + val minecraftNext by lazy { + val mcNext = when (val part = minecraft.parts.getOrNull(1)) { + // Mimics the code used to get the next Minecraft version in Forge's MDK + // https://github.com/MinecraftForge/MinecraftForge/blob/0ff8a596fc1ef33d4070be89dd5cb4851f93f731/build.gradle#L884 + is SemanticVersion.Companion.VersionPart.ReleasePart -> (part.version + 1).toString() + null -> "?" + else -> part.versionString + } + + "1.$mcNext" + } + + val hasForge: Boolean by lazy { !forge?.parts.isNullOrEmpty() } + val forgeSpec: String? by lazy { forge?.parts?.getOrNull(0)?.versionString } + + val hasNeoforge: Boolean by lazy { !neoforge?.parts.isNullOrEmpty() } + val neoforgeSpec: String? by lazy { neoforge?.parts?.getOrNull(0)?.versionString } +} diff --git a/src/main/kotlin/creator/custom/model/ClassFqn.kt b/src/main/kotlin/creator/custom/model/ClassFqn.kt index 783948836..5383f3fac 100644 --- a/src/main/kotlin/creator/custom/model/ClassFqn.kt +++ b/src/main/kotlin/creator/custom/model/ClassFqn.kt @@ -45,5 +45,7 @@ data class ClassFqn(val fqn: String) { fun withClassName(className: String) = copy("$packageName.$className") + fun withSubPackage(name: String) = copy("$packageName.$name.$className") + override fun toString(): String = fqn } diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt new file mode 100644 index 000000000..64f6cc623 --- /dev/null +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt @@ -0,0 +1,450 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.types + +import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.creator.custom.BuiltinValidations +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.model.ArchitecturyVersionsModel +import com.demonwav.mcdev.platform.architectury.ArchitecturyVersion +import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions +import com.demonwav.mcdev.platform.fabric.util.FabricVersions +import com.demonwav.mcdev.platform.forge.version.ForgeVersion +import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion +import com.demonwav.mcdev.util.MinecraftVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.demonwav.mcdev.util.asyncIO +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.and +import com.intellij.openapi.observable.util.not +import com.intellij.openapi.observable.util.transform +import com.intellij.ui.ComboboxSpeedSearch +import com.intellij.ui.JBColor +import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.bindItem +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.util.application +import javax.swing.DefaultComboBoxModel +import javax.swing.JComboBox +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext + +class ArchitecturyVersionsProperty( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> +) : CreatorProperty(descriptor, graph, properties) { + + private val emptyVersion = SemanticVersion.release() + private val emptyValue = ArchitecturyVersionsModel( + emptyVersion, + emptyVersion, + emptyVersion, + emptyVersion, + emptyVersion, + FabricVersions.YarnVersion("", -1), + true, + emptyVersion, + true, + true, + emptyVersion, + ) + private val defaultValue = createDefaultValue(descriptor.default) + + private var forgeVersions: ForgeVersion? = null + private var neoForgeVersions: NeoForgeVersion? = null + private var fabricVersions: FabricVersions? = null + private var loomVersions: List? = null + private var fabricApiVersions: FabricApiVersions? = null + private var architecturyVersions: ArchitecturyVersion? = null + + override val graphProperty: GraphProperty = graph.property(defaultValue) + var model: ArchitecturyVersionsModel by graphProperty + + val mcVersionProperty = graphProperty.transform({ it.minecraft }, { model.copy(minecraft = it) }) + val mcVersionModel = DefaultComboBoxModel() + + val forgeVersionProperty = graphProperty.transform({ it.forge }, { model.copy(forge = it) }) + val forgeVersionsModel = DefaultComboBoxModel() + val isForgeAvailableProperty = forgeVersionProperty.transform { !it?.parts.isNullOrEmpty() } // graph.property(false) + + val nfVersionProperty = graphProperty.transform({ it.neoforge }, { model.copy(neoforge = it) }) + val nfVersionsModel = DefaultComboBoxModel() + val isNfAvailableProperty = nfVersionProperty.transform { !it?.parts.isNullOrEmpty() } // graph.property(false) + + val loomVersionProperty = graphProperty.transform({ it.loom }, { model.copy(loom = it) }) + val loomVersionModel = DefaultComboBoxModel() + + val loaderVersionProperty = graphProperty.transform({ it.loader }, { model.copy(loader = it) }) + val loaderVersionModel = DefaultComboBoxModel() + + val yarnVersionProperty = graphProperty.transform({ it.yarn }, { model.copy(yarn = it) }) + val yarnVersionModel = DefaultComboBoxModel() + val yarnHasMatchingGameVersion = mcVersionProperty.transform { mcVersion -> + val versions = fabricVersions + ?: return@transform true + val mcVersionString = mcVersion.toString() + versions.mappings.any { it.gameVersion == mcVersionString } + } + + val fabricApiVersionProperty = graphProperty.transform({ it.fabricApi }, { model.copy(fabricApi = it) }) + val fabricApiVersionModel = DefaultComboBoxModel() + val useFabricApiVersionProperty = graphProperty.transform({ it.useFabricApi }, { model.copy(useFabricApi = it) }) + val fabricApiHasMatchingGameVersion = mcVersionProperty.transform { mcVersion -> + val apiVersions = fabricApiVersions + ?: return@transform true + val mcVersionString = mcVersion.toString() + apiVersions.versions.any { mcVersionString in it.gameVersions } + } + + val useOfficialMappingsProperty = + graphProperty.transform({ it.useOfficialMappings }, { model.copy(useOfficialMappings = it) }) + + val architecturyApiVersionProperty = + graphProperty.transform({ it.architecturyApi }, { model.copy(architecturyApi = it) }) + val architecturyApiVersionModel = DefaultComboBoxModel() + val useArchitecturyApiVersionProperty = + graphProperty.transform({ it.useArchitecturyApi }, { model.copy(useArchitecturyApi = it) }) + val architecturyApiHasMatchingGameVersion = mcVersionProperty.transform { mcVersion -> + val apiVersions = architecturyVersions + ?: return@transform true + apiVersions.versions.containsKey(mcVersion) + } + + override fun createDefaultValue(raw: Any?): ArchitecturyVersionsModel = when (raw) { + is String -> deserialize(raw) + else -> emptyValue + } + + override fun serialize(value: ArchitecturyVersionsModel): String { + return "${value.minecraft} ${value.forge} ${value.neoforge} ${value.loom} ${value.loader} ${value.yarn}" + + " ${value.useFabricApi} ${value.fabricApi} ${value.useOfficialMappings} ${value.useArchitecturyApi}" + + " ${value.architecturyApi}" + } + + override fun deserialize(string: String): ArchitecturyVersionsModel { + val segments = string.split(' ') + val yarnSegments = segments.getOrNull(5)?.split(':') + val yarnVersion = if (yarnSegments != null && yarnSegments.size == 2) { + FabricVersions.YarnVersion(yarnSegments[0], yarnSegments[1].toInt()) + } else { + emptyValue.yarn + } + return ArchitecturyVersionsModel( + segments.getOrNull(0)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(1)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(2)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(3)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(4)?.let(SemanticVersion::tryParse) ?: emptyVersion, + yarnVersion, + segments.getOrNull(6)?.toBoolean() != false, + segments.getOrNull(7)?.let(SemanticVersion::tryParse) ?: emptyVersion, + segments.getOrNull(8)?.toBoolean() != false, + segments.getOrNull(9)?.toBoolean() != false, + segments.getOrNull(10)?.let(SemanticVersion::tryParse) ?: emptyVersion, + ) + } + + override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("Minecraft Version:") { + comboBox(mcVersionModel) + .bindItem(mcVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + panel.row("Forge Version:") { + comboBox(forgeVersionsModel) + .bindItem(forgeVersionProperty) + .enabledIf(isForgeAvailableProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + .component + }.enabled(descriptor.editable != false) + + panel.row("NeoForge Version:") { + comboBox(nfVersionsModel) + .bindItem(nfVersionProperty) + .enabledIf(isNfAvailableProperty) + .also { ComboboxSpeedSearch.installOn(it.component) } + .component + }.enabled(descriptor.editable != false) + + // + // panel.row("Loom Version:") { + // comboBox(loomVersionModel) + // .bindItem(loomVersionProperty) + // .validationOnInput(BuiltinValidations.nonEmptyVersion) + // .validationOnApply(BuiltinValidations.nonEmptyVersion) + // .also { ComboboxSpeedSearch.installOn(it.component) } + // }.enabled(descriptor.editable != false) + + panel.row("Loader Version:") { + comboBox(loaderVersionModel) + .bindItem(loaderVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + }.enabled(descriptor.editable != false) + + // Official mappings forced currently, yarn mappings are not handled yet + // panel.row("Yarn Version:") { + // comboBox(yarnVersionModel) + // .bindItem(yarnVersionProperty) + // .enabledIf(useOfficialMappingsProperty.not()) + // .validationOnInput(BuiltinValidations.nonEmptyYarnVersion) + // .validationOnApply(BuiltinValidations.nonEmptyYarnVersion) + // .also { ComboboxSpeedSearch.installOn(it.component) } + // + // checkBox("Use official mappings") + // .bindSelected(useOfficialMappingsProperty) + // + // label("Unable to match Yarn versions to Minecraft version") + // .visibleIf(yarnHasMatchingGameVersion.not()) + // .component.foreground = JBColor.YELLOW + // }.enabled(descriptor.editable != false) + + panel.row("FabricApi Version:") { + comboBox(fabricApiVersionModel) + .bindItem(fabricApiVersionProperty) + .enabledIf(useFabricApiVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + + checkBox("Use FabricApi") + .bindSelected(useFabricApiVersionProperty) + label("Unable to match API versions to Minecraft version") + .visibleIf(fabricApiHasMatchingGameVersion.not()) + .component.foreground = JBColor.YELLOW + } + + panel.row("ArchitecturyApi Version:") { + comboBox(architecturyApiVersionModel) + .bindItem(architecturyApiVersionProperty) + .enabledIf(useArchitecturyApiVersionProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) + .also { ComboboxSpeedSearch.installOn(it.component) } + + checkBox("Use ArchitecturyApi") + .bindSelected(useArchitecturyApiVersionProperty) + label("Unable to match API versions to Minecraft version") + .visibleIf(architecturyApiHasMatchingGameVersion.not()) + .component.foreground = JBColor.YELLOW + }.enabled(descriptor.editable != false) + } + + override fun setupProperty() { + super.setupProperty() + + var previousMcVersion: SemanticVersion? = null + mcVersionProperty.afterChange { mcVersion -> + if (previousMcVersion == mcVersion) { + return@afterChange + } + + previousMcVersion = mcVersion + + updateForgeVersions() + updateNeoForgeVersions() + updateYarnVersions() + updateFabricApiVersions() + updateArchitecturyApiVersions() + } + + application.executeOnPooledThread { + runBlocking { + val forgeVersionsJob = asyncIO { ForgeVersion.downloadData() } + val neoForgeVersionsJob = asyncIO { NeoForgeVersion.downloadData() } + val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } + val loomVersionsJob = asyncIO { + collectMavenVersions("https://maven.architectury.dev/dev/architectury/architectury-loom//maven-metadata.xml") + } + val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } + val architecturyVersionsJob = asyncIO { ArchitecturyVersion.downloadData() } + + this@ArchitecturyVersionsProperty.forgeVersions = forgeVersionsJob.await() + this@ArchitecturyVersionsProperty.neoForgeVersions = neoForgeVersionsJob.await() + this@ArchitecturyVersionsProperty.fabricVersions = fabricVersionsJob.await() + this@ArchitecturyVersionsProperty.loomVersions = loomVersionsJob.await() + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + this@ArchitecturyVersionsProperty.fabricApiVersions = fabricApiVersionsJob.await() + this@ArchitecturyVersionsProperty.architecturyVersions = architecturyVersionsJob.await() + + withContext(Dispatchers.Swing) { + val fabricVersions = fabricVersions + if (fabricVersions != null) { + loaderVersionModel.removeAllElements() + loaderVersionModel.addAll(fabricVersions.loader) + loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) + } + + val loomVersions = loomVersions + if (loomVersions != null) { + loomVersionModel.removeAllElements() + loomVersionModel.addAll(loomVersions) + val defaultValue = loomVersions.find { + it.parts.any { it is SemanticVersion.Companion.VersionPart.PreReleasePart } + } ?: loomVersions.firstOrNull() ?: emptyVersion + + loomVersionProperty.set(defaultValue) + } + + updateMcVersionsList() + } + } + } + } + + private fun updateMcVersionsList() { + val architecturyVersions = architecturyVersions + ?: return + + val mcVersions = architecturyVersions.versions.keys.sortedDescending() + mcVersionModel.removeAllElements() + mcVersionModel.addAll(mcVersions) + + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraft in mcVersions -> defaultValue.minecraft + else -> mcVersions.first() + } + mcVersionProperty.set(selectedMcVersion) + } + + private fun updateForgeVersions() { + val mcVersion = mcVersionProperty.get() + + val filterExpr = descriptor.parameters?.get("forgeMcVersionFilter") as? String + if (filterExpr != null) { + val conditionProps = mapOf("MC_VERSION" to mcVersion) + if (!TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true)) { + forgeVersionsModel.removeAllElements() + application.invokeLater { + // For some reason we have to set those properties later for the values to actually be set + // and the enabled state to be set appropriately + forgeVersionProperty.set(null) + } + return + } + } + + val availableForgeVersions = forgeVersions!!.getForgeVersions(mcVersion) + .take(descriptor.limit ?: 50) + forgeVersionsModel.removeAllElements() + forgeVersionsModel.addAll(availableForgeVersions) + application.invokeLater { + forgeVersionProperty.set(availableForgeVersions.firstOrNull()) + } + } + + private fun updateNeoForgeVersions() { + val mcVersion = mcVersionProperty.get() + + val filterExpr = descriptor.parameters?.get("neoForgeMcVersionFilter") as? String + if (filterExpr != null) { + val conditionProps = mapOf("MC_VERSION" to mcVersion) + if (!TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true)) { + nfVersionsModel.removeAllElements() + application.invokeLater { + nfVersionProperty.set(null) + } + return + } + } + + val availableNeoForgeVersions = neoForgeVersions!!.getNeoForgeVersions(mcVersion) + .take(descriptor.limit ?: 50) + nfVersionsModel.removeAllElements() + nfVersionsModel.addAll(availableNeoForgeVersions) + application.invokeLater { + nfVersionProperty.set(availableNeoForgeVersions.firstOrNull()) + } + } + + private fun updateYarnVersions() { + val fabricVersions = fabricVersions + ?: return + + val mcVersion = mcVersionProperty.get() + val mcVersionString = mcVersion.toString() + + val yarnVersions = if (yarnHasMatchingGameVersion.get()) { + fabricVersions.mappings.asSequence() + .filter { it.gameVersion == mcVersionString } + .map { it.version } + .toList() + } else { + fabricVersions.mappings.map { it.version } + } + yarnVersionModel.removeAllElements() + yarnVersionModel.addAll(yarnVersions) + yarnVersionProperty.set(yarnVersions.firstOrNull() ?: emptyValue.yarn) + } + + private fun updateFabricApiVersions() { + val fabricApiVersions = fabricApiVersions + ?: return + + val mcVersion = mcVersionProperty.get() + val mcVersionString = mcVersion.toString() + + val apiVersions = if (fabricApiHasMatchingGameVersion.get()) { + fabricApiVersions.versions.asSequence() + .filter { mcVersionString in it.gameVersions } + .map(FabricApiVersions.Version::version) + .toList() + } else { + fabricApiVersions.versions.map(FabricApiVersions.Version::version) + } + fabricApiVersionModel.removeAllElements() + fabricApiVersionModel.addAll(apiVersions) + fabricApiVersionProperty.set(apiVersions.firstOrNull() ?: emptyVersion) + } + + private fun updateArchitecturyApiVersions() { + val architecturyVersions = architecturyVersions + ?: return + + val mcVersion = mcVersionProperty.get() + val availableArchitecturyApiVersions = architecturyVersions.getArchitecturyVersions(mcVersion) + architecturyApiVersionModel.removeAllElements() + architecturyApiVersionModel.addAll(availableArchitecturyApiVersions) + + architecturyApiVersionProperty.set(availableArchitecturyApiVersions.firstOrNull() ?: emptyVersion) + } + + class Factory : CreatorPropertyFactory { + + override fun create( + graph: PropertyGraph, + descriptor: TemplatePropertyDescriptor, + properties: Map> + ): CreatorProperty<*> = ArchitecturyVersionsProperty(graph, descriptor, properties) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 91fbadea6..95042f193 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -132,6 +132,7 @@ + From fd2f51c8710d71c3fc72721cbb2b1bfb614b5498 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 18:04:26 +0200 Subject: [PATCH 059/118] Add paper manifest warning --- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 1 + .../kotlin/creator/custom/types/BooleanCreatorProperty.kt | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index c7b7dc1a1..1b83aa16a 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -44,6 +44,7 @@ data class TemplatePropertyDescriptor( val hidden: Boolean? = null, val editable: Boolean? = null, val collapsible: Boolean? = null, + val warning: String? = null, val default: Any, val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index 5229e4556..b400a4f05 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -21,8 +21,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.intellij.icons.AllIcons import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.ui.content.AlertIcon import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindSelected @@ -40,6 +42,10 @@ class BooleanCreatorProperty( override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.label) { + if (descriptor.warning != null) { + icon(AlertIcon(AllIcons.General.Warning)).comment(descriptor.warning) + } + this.checkBox(descriptor.label.removeSuffix(":")) .bindSelected(graphProperty) .enabled(descriptor.editable != false) From 83e216aa8d44449bbf36aa95cbadf0377d7cae88 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 18:11:27 +0200 Subject: [PATCH 060/118] Fix ktlint warnings --- .../custom/types/ArchitecturyVersionsProperty.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt index 64f6cc623..6ad0d36f7 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt @@ -30,13 +30,11 @@ import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions import com.demonwav.mcdev.platform.fabric.util.FabricVersions import com.demonwav.mcdev.platform.forge.version.ForgeVersion import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion -import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.asyncIO import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph -import com.intellij.openapi.observable.util.and import com.intellij.openapi.observable.util.not import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch @@ -46,7 +44,6 @@ import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.util.application import javax.swing.DefaultComboBoxModel -import javax.swing.JComboBox import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing @@ -89,11 +86,11 @@ class ArchitecturyVersionsProperty( val forgeVersionProperty = graphProperty.transform({ it.forge }, { model.copy(forge = it) }) val forgeVersionsModel = DefaultComboBoxModel() - val isForgeAvailableProperty = forgeVersionProperty.transform { !it?.parts.isNullOrEmpty() } // graph.property(false) + val isForgeAvailableProperty = forgeVersionProperty.transform { !it?.parts.isNullOrEmpty() } val nfVersionProperty = graphProperty.transform({ it.neoforge }, { model.copy(neoforge = it) }) val nfVersionsModel = DefaultComboBoxModel() - val isNfAvailableProperty = nfVersionProperty.transform { !it?.parts.isNullOrEmpty() } // graph.property(false) + val isNfAvailableProperty = nfVersionProperty.transform { !it?.parts.isNullOrEmpty() } val loomVersionProperty = graphProperty.transform({ it.loom }, { model.copy(loom = it) }) val loomVersionModel = DefaultComboBoxModel() @@ -282,7 +279,9 @@ class ArchitecturyVersionsProperty( val neoForgeVersionsJob = asyncIO { NeoForgeVersion.downloadData() } val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } val loomVersionsJob = asyncIO { - collectMavenVersions("https://maven.architectury.dev/dev/architectury/architectury-loom//maven-metadata.xml") + collectMavenVersions( + "https://maven.architectury.dev/dev/architectury/architectury-loom/maven-metadata.xml" + ) } val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } val architecturyVersionsJob = asyncIO { ArchitecturyVersion.downloadData() } From ac172c7febd9385b8f70bca8e91084e68a6dbce4 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 19:12:50 +0200 Subject: [PATCH 061/118] Include templates repo as submodule --- .gitmodules | 4 ++++ templates | 1 + 2 files changed, 5 insertions(+) create mode 100644 .gitmodules create mode 160000 templates diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e5298de24 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "templates"] + path = templates + branch = main + url = https://github.com/RedNesto/mcdev-templates diff --git a/templates b/templates new file mode 160000 index 000000000..916c36226 --- /dev/null +++ b/templates @@ -0,0 +1 @@ +Subproject commit 916c3622660f6a16feae7af7e9348ded90ad4062 From 02d85e7e2e79f07cd8e243306bc313acf72e0f9b Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 20:04:53 +0200 Subject: [PATCH 062/118] Include templates in publish workflow --- .github/workflows/publish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f58ca5f03..b0908e67e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,6 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + submodules: true + - name: Fetch latest submodule updates + run: git submodule update --remote - uses: actions/setup-java@v3 with: distribution: 'zulu' From 1be51a32c2981804f4626c3d26e2299ac4502e84 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 21:03:59 +0200 Subject: [PATCH 063/118] Bump templates submodule --- templates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates b/templates index 916c36226..920f37da1 160000 --- a/templates +++ b/templates @@ -1 +1 @@ -Subproject commit 916c3622660f6a16feae7af7e9348ded90ad4062 +Subproject commit 920f37da16fd8c4c84ae329572dba1654350c050 From 2e7c24aff463dcdeb709ecb6515aef4267b8e40a Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 21:06:00 +0200 Subject: [PATCH 064/118] Switch builtin url to org repo --- .../kotlin/creator/custom/providers/BuiltInTemplateProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 0dccf5626..26e3b4b5f 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -51,7 +51,7 @@ class BuiltInTemplateProvider : TemplateProvider { indicator.text2 = "Updating builtin templates" val manager = FuelManager() - val url = "https://github.com/RedNesto/mcdev-templates/archive/refs/heads/main.zip" + val url = "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip" manager.proxy = selectProxy(url) From dfb519c30ed30bb3b1d15a2d068fb1cc85c946d5 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 3 Jun 2024 23:06:18 +0200 Subject: [PATCH 065/118] Fix directory name in builtin provider --- .../kotlin/creator/custom/providers/BuiltInTemplateProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 26e3b4b5f..545dbfd77 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -70,7 +70,7 @@ class BuiltInTemplateProvider : TemplateProvider { zipPath.writeBytes(data) FileUtil.deleteRecursively(builtinTemplatesPath) ZipUtil.extract(zipPath, builtinTemplatesPath, null) - for (child in builtinTemplatesPath.resolve("mcdev-templates-main").listDirectoryEntries()) { + for (child in builtinTemplatesPath.resolve("templates-main").listDirectoryEntries()) { child.moveTo(builtinTemplatesPath.resolve(child.fileName)) } From dda1678b015a110aa8dc2dd4bacd412404361af2 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 4 Jun 2024 13:12:03 +0200 Subject: [PATCH 066/118] Explicitly import Gradle and Maven projects --- .../creator/custom/CustomPlatformStep.kt | 15 +---- .../ImportGradleProjectFinalizer.kt | 42 +++++++++++++ .../finalizers/ImportMavenProjectFinalizer.kt | 59 +++++++++++++++++++ .../finalizers/RunGradleTasksFinalizer.kt | 8 ++- src/main/resources/META-INF/plugin.xml | 2 + 5 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt create mode 100644 src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 9ebab8257..af394aeca 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -353,18 +353,9 @@ class CustomPlatformStep( return } - val projectId = ExternalSystemTaskId.getProjectId(project) - val listener = object : ExternalSystemTaskNotificationListenerAdapter() { - override fun onSuccess(id: ExternalSystemTaskId) { - if (id.type == ExternalSystemTaskType.RESOLVE_PROJECT && projectId == id.ideProjectId) { - application.executeOnPooledThread { - // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that - CreatorFinalizer.executeAll(project, finalizers, assets.templateProperties) - } - ExternalSystemProgressNotificationManager.getInstance().removeNotificationListener(this) - } - } + application.executeOnPooledThread { + // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that + CreatorFinalizer.executeAll(project, finalizers, assets.templateProperties) } - ExternalSystemProgressNotificationManager.getInstance().addNotificationListener(listener) } } diff --git a/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt new file mode 100644 index 000000000..5048f7e22 --- /dev/null +++ b/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt @@ -0,0 +1,42 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.finalizers + +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir +import kotlin.io.path.absolutePathString +import org.jetbrains.plugins.gradle.service.project.open.canLinkAndRefreshGradleProject +import org.jetbrains.plugins.gradle.service.project.open.linkAndRefreshGradleProject + +class ImportGradleProjectFinalizer : CreatorFinalizer { + + override fun execute(project: Project, properties: Map, templateProperties: Map) { + val projectDir = project.guessProjectDir()?.toNioPath()?.absolutePathString() + ?: return + val canLink = canLinkAndRefreshGradleProject(projectDir, project, showValidationDialog = false) + thisLogger().info("canLink = $canLink projectDir = $projectDir") + if (canLink) { + linkAndRefreshGradleProject(projectDir, project) + thisLogger().info("Linking done") + } + } +} diff --git a/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt new file mode 100644 index 000000000..78e565fbc --- /dev/null +++ b/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt @@ -0,0 +1,59 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.finalizers + +import com.demonwav.mcdev.util.invokeAndWait +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.vfs.VfsUtil +import java.nio.file.Path +import java.util.concurrent.TimeUnit +import kotlin.io.path.absolutePathString +import org.jetbrains.idea.maven.project.importing.MavenImportingManager + +class ImportMavenProjectFinalizer : CreatorFinalizer { + + override fun execute(project: Project, properties: Map, templateProperties: Map) { + val projectDir = project.guessProjectDir()?.toNioPath()?.absolutePathString() + ?: return + + val pomFile = VfsUtil.findFile(Path.of(projectDir).resolve("pom.xml"), true) + ?: return + thisLogger().info("Invoking import on EDT pomFile = ${pomFile.path}") + val promise = invokeAndWait { + if (project.isDisposed || !project.isInitialized) { + return@invokeAndWait null + } + + MavenImportingManager.getInstance(project).linkAndImportFile(pomFile) + } + + if (promise == null) { + thisLogger().info("Could not start import") + return + } + + thisLogger().info("Waiting for import to finish") + promise.finishPromise.blockingGet(Int.MAX_VALUE, TimeUnit.SECONDS) + thisLogger().info("Import finished") + } +} diff --git a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt index 22bbd4cc3..bef854b34 100644 --- a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.util.runGradleTaskAndWait +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir @@ -30,9 +31,14 @@ class RunGradleTasksFinalizer : CreatorFinalizer { @Suppress("UNCHECKED_CAST") val tasks = properties["tasks"] as? List ?: return + val projectDir = project.guessProjectDir()?.toNioPath() + ?: return - runGradleTaskAndWait(project, project.guessProjectDir()!!.toNioPath()) { settings -> + thisLogger().info("tasks = $tasks projectDir = $projectDir") + runGradleTaskAndWait(project, projectDir) { settings -> settings.taskNames = tasks } + + thisLogger().info("Done running tasks") } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 95042f193..c1eb8c96d 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -153,6 +153,8 @@ + + From a50757983f6d3f7a8806ed692916f2595faf3dee Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 4 Jun 2024 14:10:59 +0200 Subject: [PATCH 067/118] Remove unused imports --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 4 ---- templates | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index af394aeca..aa817f6b2 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -38,10 +38,6 @@ import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId -import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter -import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType -import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator diff --git a/templates b/templates index 920f37da1..3d60a1e2f 160000 --- a/templates +++ b/templates @@ -1 +1 @@ -Subproject commit 920f37da16fd8c4c84ae329572dba1654350c050 +Subproject commit 3d60a1e2f7ba5947eab45b33a36121e4e9c91b3e From 8766ca8115ef373991b336efcca2dced8e6bffc7 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 6 Jun 2024 20:39:52 +0200 Subject: [PATCH 068/118] Use org repo --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e5298de24..e7f35c399 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "templates"] path = templates branch = main - url = https://github.com/RedNesto/mcdev-templates + url = https://github.com/MinecraftDev/templates From 28f38c526a762cf01b57ac2cd6760562dc1ea71e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 6 Jun 2024 20:54:59 +0200 Subject: [PATCH 069/118] Promote new wizard I'd like to keep the old one for some time until all the new templates are proven to be fully working --- src/main/kotlin/creator/MinecraftModuleBuilder.kt | 2 +- src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt | 2 +- src/main/resources/META-INF/plugin.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/MinecraftModuleBuilder.kt b/src/main/kotlin/creator/MinecraftModuleBuilder.kt index 7b3f2318c..a847ccf14 100644 --- a/src/main/kotlin/creator/MinecraftModuleBuilder.kt +++ b/src/main/kotlin/creator/MinecraftModuleBuilder.kt @@ -35,7 +35,7 @@ import com.intellij.openapi.roots.ModifiableRootModel class MinecraftModuleBuilder : AbstractNewProjectWizardBuilder() { - override fun getPresentableName() = "Minecraft" + override fun getPresentableName() = "Minecraft (Old Wizard)" override fun getNodeIcon() = PlatformAssets.MINECRAFT_ICON override fun getGroupName() = "Minecraft" override fun getBuilderId() = "MINECRAFT_MODULE" diff --git a/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt index 60dd29e34..65a95052e 100644 --- a/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt +++ b/src/main/kotlin/creator/custom/CustomMinecraftModuleBuilder.kt @@ -33,7 +33,7 @@ import com.intellij.openapi.roots.ModifiableRootModel class CustomMinecraftModuleBuilder : AbstractNewProjectWizardBuilder() { - override fun getPresentableName() = "Minecraft Custom Template" + override fun getPresentableName() = "Minecraft" override fun getNodeIcon() = PlatformAssets.MINECRAFT_ICON override fun getGroupName() = "Minecraft" override fun getBuilderId() = "CUSTOM_MINECRAFT_MODULE" diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index c1eb8c96d..deeff5384 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -216,8 +216,8 @@ - + From e17a4f7d5529273daf73990a1628cf1203e39baf Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 6 Jun 2024 21:10:37 +0200 Subject: [PATCH 070/118] Actually use the correct org name I swear... --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e7f35c399..ed1c5f036 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "templates"] path = templates branch = main - url = https://github.com/MinecraftDev/templates + url = https://github.com/minecraft-dev/templates From a29a4f43274a96b19c45fe868d812ee7b6eb32d9 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 6 Jun 2024 22:59:32 +0200 Subject: [PATCH 071/118] Get rid of AbstractLongRunningAssetsStep usage Also improve robustness of the creator --- .../creator/custom/CustomPlatformStep.kt | 88 +++++++++---------- .../custom/finalizers/CreatorFinalizer.kt | 8 +- .../providers/BuiltInTemplateProvider.kt | 8 +- .../custom/providers/TemplateProvider.kt | 11 ++- .../custom/types/StringCreatorProperty.kt | 8 +- 5 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index aa817f6b2..daaeec9c6 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -29,12 +29,11 @@ import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty -import com.demonwav.mcdev.creator.step.AbstractLongRunningAssetsStep -import com.intellij.ide.fileTemplates.impl.CustomFileTemplate -import com.intellij.ide.starters.local.GeneratorTemplateFile +import com.intellij.ide.wizard.AbstractNewProjectWizardStep import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.thisLogger @@ -44,7 +43,6 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project -import com.intellij.openapi.util.io.FileUtilRt import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder @@ -52,21 +50,20 @@ import com.intellij.ui.dsl.builder.SegmentedButton import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import com.intellij.util.application -import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set +import kotlin.io.path.createDirectories +import kotlin.io.path.writeText /** * The step to select a custom template repo. */ class CustomPlatformStep( parent: NewProjectWizardStep, -) : AbstractLongRunningAssetsStep(parent) { - - override val description: String = MCDevBundle("creator.ui.custom.step.description") +) : AbstractNewProjectWizardStep(parent) { val templateProviders = TemplateProvider.getAll() @@ -219,8 +216,11 @@ class CustomPlatformStep( .mapNotNull { setupProperty(it) } .sortedBy { (_, order) -> order } .map { it.first } - } catch (e: Throwable) { - thisLogger().error(e) + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + thisLogger().error(t) emptyList() } } @@ -278,7 +278,7 @@ class CustomPlatformStep( return factory to order } - override fun setupAssets(project: Project) { + override fun setupProject(project: Project) { val template = selectedTemplate if (template is EmptyLoadedTemplate) { return @@ -288,26 +288,20 @@ class CustomPlatformStep( RecentProjectTemplates.instance.addNewTemplate(templateProvider.javaClass.name, template) } - val descriptor = template.descriptor - - collectTemplateProperties(assets.templateProperties) - - thisLogger().debug("Template properties: ${assets.templateProperties}") - - val baseData = data.getUserData(NewProjectWizardBaseData.KEY) - ?: return thisLogger().error("Could not find wizard base data") - val projectPath = Path.of(baseData.path) + val projectPath = context.projectDirectory + val templateProperties = collectTemplateProperties() + thisLogger().debug("Template properties: $templateProperties") - for (file in descriptor.files.orEmpty()) { + for (file in template.descriptor.files.orEmpty()) { if (file.condition != null && - !TemplateEvaluator.condition(assets.templateProperties, file.condition).getOrElse { false } + !TemplateEvaluator.condition(templateProperties, file.condition).getOrElse { false } ) { continue } - val relativeTemplate = TemplateEvaluator.template(assets.templateProperties, file.template).getOrNull() + val relativeTemplate = TemplateEvaluator.template(templateProperties, file.template).getOrNull() ?: continue - val relativeDest = TemplateEvaluator.template(assets.templateProperties, file.destination).getOrNull() + val relativeDest = TemplateEvaluator.template(templateProperties, file.destination).getOrNull() ?: continue try { @@ -320,19 +314,33 @@ class CustomPlatformStep( continue } - val fileName = destPath.fileName.toString().removeSuffix(".ft") - val baseFileName = FileUtilRt.getNameWithoutExtension(fileName) - val extension = FileUtilRt.getExtension(fileName) - val fileTemplate = CustomFileTemplate(baseFileName, extension) - fileTemplate.text = templateContents - assets.addAssets(GeneratorTemplateFile(projectPath.relativize(destPath).toString(), fileTemplate)) - } catch (e: Exception) { - thisLogger().error("Failed to process template file $file", e) + val processedContent = TemplateEvaluator.template(templateProperties, templateContents) + .getOrLogException(thisLogger()) + ?: continue + + destPath.parent.createDirectories() + destPath.writeText(processedContent) + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + + thisLogger().error("Failed to process template file $file", t) + } + } + + val finalizers = selectedTemplate.descriptor.finalizers + if (!finalizers.isNullOrEmpty()) { + application.executeOnPooledThread { + // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that + CreatorFinalizer.executeAll(project, finalizers, templateProperties) } } } - private fun collectTemplateProperties(into: MutableMap = mutableMapOf()): MutableMap { + private fun collectTemplateProperties(): MutableMap { + val into = mutableMapOf() + into.putAll(TemplateEvaluator.baseProperties) val gitData = data.getUserData(GitNewProjectWizardData.KEY) @@ -340,18 +348,4 @@ class CustomPlatformStep( return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } - - override fun perform(project: Project) { - super.perform(project) - - val finalizers = selectedTemplate.descriptor.finalizers - if (finalizers.isNullOrEmpty()) { - return - } - - application.executeOnPooledThread { - // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that - CreatorFinalizer.executeAll(project, finalizers, assets.templateProperties) - } - } } diff --git a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt index c05551d9c..1f20aa339 100644 --- a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.RequiredElement @@ -62,8 +63,11 @@ interface CreatorFinalizer { try { finalizer.execute(project, properties, templateProperties) - } catch (e: Exception) { - thisLogger().error("Unhandled exception in finalizer $type", e) + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + thisLogger().error("Unhandled exception in finalizer $type", t) } } } diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 545dbfd77..8480aa8c7 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -28,6 +28,7 @@ import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.onError import com.intellij.ide.util.projectWizard.WizardContext + import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.progress.ProgressIndicator @@ -76,8 +77,11 @@ class BuiltInTemplateProvider : TemplateProvider { updatedBuiltinTemplates = true thisLogger().info("Builtin template update applied successfully") - } catch (e: Exception) { - thisLogger().error("Failed to apply builtin templates update", e) + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + thisLogger().error("Failed to apply builtin templates update", t) } } } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 51bc6a7c6..d0e318c7c 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -25,6 +25,7 @@ import com.demonwav.mcdev.util.fromJson import com.google.gson.Gson import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.Attachment +import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.PropertyGraph @@ -74,12 +75,16 @@ interface TemplateProvider { } else if (child.name.endsWith(".mcdev.template.json")) { try { createVfsLoadedTemplate(directory, child)?.let(templates::add) - } catch (e: Throwable) { + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + val attachment = runCatching { Attachment(child.name, child.readText()) }.getOrNull() if (attachment != null) { - thisLogger().error("Failed to load template ${child.path}", e, attachment) + thisLogger().error("Failed to load template ${child.path}", t, attachment) } else { - thisLogger().error("Failed to load template ${child.path}", e) + thisLogger().error("Failed to load template ${child.path}", t) } } } diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 56fc06f26..07052162d 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph @@ -80,9 +81,12 @@ class StringCreatorProperty( val regex = regexString.toRegex() textField.textValidation(BuiltinValidations.byRegex(regex)) } - } catch (e: Exception) { + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } logger() - .error("Failed to create validator for property ${descriptor.name}", e) + .error("Failed to create validator for property ${descriptor.name}", t) } }.visible(descriptor.hidden != true) } From b027601b49c487c6eddcba3c939fb9ec975e4e24 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 7 Jun 2024 01:10:58 +0200 Subject: [PATCH 072/118] Reformat and open main files --- .../creator/custom/CustomPlatformStep.kt | 73 ++++++++++++++++++- .../creator/custom/TemplateDescriptor.kt | 3 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index daaeec9c6..fd51f694f 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -29,6 +29,10 @@ import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty +import com.demonwav.mcdev.util.toTypedArray +import com.demonwav.mcdev.util.virtualFileOrError +import com.intellij.codeInsight.actions.ReformatCodeProcessor +import com.intellij.ide.projectView.ProjectView import com.intellij.ide.wizard.AbstractNewProjectWizardStep import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData @@ -37,12 +41,21 @@ import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.module.ModuleTypeId import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ModuleRootManager +import com.intellij.openapi.startup.StartupManager +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.refreshAndFindVirtualFile +import com.intellij.psi.PsiManager import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder @@ -50,6 +63,7 @@ import com.intellij.ui.dsl.builder.SegmentedButton import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import com.intellij.util.application +import java.nio.file.Path import java.util.function.Consumer import javax.swing.JComponent import kotlin.collections.component1 @@ -292,6 +306,7 @@ class CustomPlatformStep( val templateProperties = collectTemplateProperties() thisLogger().debug("Template properties: $templateProperties") + val generatedFiles = mutableListOf>() for (file in template.descriptor.files.orEmpty()) { if (file.condition != null && !TemplateEvaluator.condition(templateProperties, file.condition).getOrElse { false } @@ -320,6 +335,13 @@ class CustomPlatformStep( destPath.parent.createDirectories() destPath.writeText(processedContent) + + val virtualFile = destPath.refreshAndFindVirtualFile() + if (virtualFile != null) { + generatedFiles.add(file to virtualFile) + } else { + thisLogger().warn("Could not find VirtualFile for file generated at $destPath (descriptor: $file)") + } } catch (t: Throwable) { if (t is ControlFlowException) { throw t @@ -329,15 +351,33 @@ class CustomPlatformStep( } } - val finalizers = selectedTemplate.descriptor.finalizers - if (!finalizers.isNullOrEmpty()) { - application.executeOnPooledThread { - // Has to be executed with a delay or else it deadlocks, the pooled thread is an easy way to achieve that + application.executeOnPooledThread { + application.invokeLater({ + application.runWriteAction { + LocalFileSystem.getInstance().refresh(false) + // Apparently a module root is required for the reformat to work + setupTempRootModule(project, projectPath) + } + reformatFiles(project, generatedFiles) + openFilesInEditor(project, generatedFiles) + }, project.disposed) + + val finalizers = selectedTemplate.descriptor.finalizers + if (!finalizers.isNullOrEmpty()) { CreatorFinalizer.executeAll(project, finalizers, templateProperties) } } } + private fun setupTempRootModule(project: Project, projectPath: Path) { + val modifiableModel = ModuleManager.getInstance(project).getModifiableModel() + val module = modifiableModel.newNonPersistentModule("mcdev-temp-root", ModuleTypeId.JAVA_MODULE) + val rootsModel = ModuleRootManager.getInstance(module).modifiableModel + rootsModel.addContentEntry(projectPath.virtualFileOrError) + rootsModel.commit() + modifiableModel.commit() + } + private fun collectTemplateProperties(): MutableMap { val into = mutableMapOf() @@ -348,4 +388,29 @@ class CustomPlatformStep( return properties.mapValuesTo(into) { (_, prop) -> prop.get() } } + + private fun reformatFiles( + project: Project, + files: MutableList> + ) { + val psiManager = PsiManager.getInstance(project) + val psiFiles = files.asSequence() + .filter { (desc, _) -> desc.reformat != false } + .mapNotNull { (_, file) -> psiManager.findFile(file) } + ReformatCodeProcessor(project, psiFiles.toTypedArray(), null, false).run() + } + + private fun openFilesInEditor( + project: Project, + files: MutableList> + ) { + val fileEditorManager = FileEditorManager.getInstance(project) + val projectView = ProjectView.getInstance(project) + for ((desc, file) in files) { + if (desc.openInEditor == true) { + fileEditorManager.openFile(file, true) + projectView.select(null, file, false) + } + } + } } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 1b83aa16a..0d26c5021 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -71,5 +71,6 @@ data class TemplateFile( val template: String, val destination: String, val condition: String? = null, - var contents: String = "", + val reformat: Boolean? = null, + val openInEditor: Boolean? = null, ) From 78d344d29afeb72e8408c5380f372e5c534c47a3 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 7 Jun 2024 01:44:00 +0200 Subject: [PATCH 073/118] Remove unused import --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index fd51f694f..48eb7385f 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -51,7 +51,6 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootManager -import com.intellij.openapi.startup.StartupManager import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.refreshAndFindVirtualFile From e9f1638ead5c6ad57e2bdda2355e0f58e333163b Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 7 Jun 2024 01:53:36 +0200 Subject: [PATCH 074/118] Specify TemplateApi target and retention --- src/main/kotlin/creator/custom/model/TemplateApi.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/creator/custom/model/TemplateApi.kt b/src/main/kotlin/creator/custom/model/TemplateApi.kt index 88c4542cb..fb053db2d 100644 --- a/src/main/kotlin/creator/custom/model/TemplateApi.kt +++ b/src/main/kotlin/creator/custom/model/TemplateApi.kt @@ -25,4 +25,6 @@ package com.demonwav.mcdev.creator.custom.model * * Be careful of not breaking source or binary compatibility of those APIs without a good reason. */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.SOURCE) annotation class TemplateApi From 48421e9ce45ed039af67ee867937071619be5a47 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 7 Jun 2024 17:30:27 +0200 Subject: [PATCH 075/118] Improve loading UI/UX --- .../custom/CreatorProgressIndicator.kt | 38 ++++ .../creator/custom/CustomPlatformStep.kt | 162 +++++++++++++----- .../providers/BuiltInTemplateProvider.kt | 2 +- .../custom/providers/TemplateProvider.kt | 9 +- .../messages/MinecraftDevelopment.properties | 4 +- 5 files changed, 168 insertions(+), 47 deletions(-) create mode 100644 src/main/kotlin/creator/custom/CreatorProgressIndicator.kt diff --git a/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt b/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt new file mode 100644 index 000000000..682310be1 --- /dev/null +++ b/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt @@ -0,0 +1,38 @@ +package com.demonwav.mcdev.creator.custom + +import com.intellij.openapi.observable.properties.GraphProperty +import com.intellij.openapi.progress.TaskInfo +import com.intellij.openapi.progress.util.ProgressIndicatorBase + +class CreatorProgressIndicator( + val loadingProperty: GraphProperty? = null, + val textProperty: GraphProperty? = null, + val text2Property: GraphProperty? = null, +) : ProgressIndicatorBase(false, false) { + + init { + loadingProperty?.set(false) + textProperty?.set("") + text2Property?.set("") + } + + override fun start() { + super.start() + loadingProperty?.set(true) + } + + override fun finish(task: TaskInfo) { + super.finish(task) + loadingProperty?.set(false) + } + + override fun setText(text: String?) { + super.setText(text) + textProperty?.set(text ?: "") + } + + override fun setText2(text: String?) { + super.setText2(text) + text2Property?.set(text ?: "") + } +} diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 48eb7385f..fe9ecafa5 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -34,9 +34,11 @@ import com.demonwav.mcdev.util.virtualFileOrError import com.intellij.codeInsight.actions.ReformatCodeProcessor import com.intellij.ide.projectView.ProjectView import com.intellij.ide.wizard.AbstractNewProjectWizardStep +import com.intellij.ide.wizard.AbstractWizard import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger @@ -44,7 +46,6 @@ import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleTypeId -import com.intellij.openapi.observable.util.or import com.intellij.openapi.observable.util.transform import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager @@ -53,18 +54,23 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.refreshAndFindVirtualFile import com.intellij.psi.PsiManager +import com.intellij.ui.JBColor import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.Cell import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Placeholder import com.intellij.ui.dsl.builder.SegmentedButton import com.intellij.ui.dsl.builder.TopGap +import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.panel import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon import java.nio.file.Path import java.util.function.Consumer -import javax.swing.JComponent +import javax.swing.JLabel import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.set @@ -90,12 +96,22 @@ class CustomPlatformStep( val selectedTemplateProperty = propertyGraph.property(EmptyLoadedTemplate) var selectedTemplate by selectedTemplateProperty + val templateProvidersLoadingProperty = propertyGraph.property(true) + val templateProvidersTextProperty = propertyGraph.property("") + val templateProvidersText2Property = propertyGraph.property("") + lateinit var templateProvidersProcessIcon: Cell + lateinit var templateProviderPlaceholder: Placeholder + + val templateLoadingProperty = propertyGraph.property(true) + val templateLoadingTextProperty = propertyGraph.property("") + val templateLoadingText2Property = propertyGraph.property("") + lateinit var templatePropertiesProcessIcon: Cell + lateinit var noTemplatesAvailable: Cell + var templateLoadingIndicator: ProgressIndicator? = null + private var properties = mutableMapOf>() override fun setupUI(builder: Panel) { - var taskParentComponent: JComponent? = null - - lateinit var templateProviderPlaceholder: Placeholder lateinit var templatePropertyPlaceholder: Placeholder builder.row(MCDevBundle("creator.ui.custom.provider.label")) { @@ -104,18 +120,26 @@ class CustomPlatformStep( } builder.row { - templateProviderPlaceholder = placeholder() - } + templateProvidersProcessIcon = + cell(AsyncProcessIcon("TemplateProviders init")) + .visibleIf(templateProvidersLoadingProperty) + label(MCDevBundle("creator.step.generic.init_template_providers.message")) + .visibleIf(templateProvidersLoadingProperty) + label("") + .bindText(templateProvidersTextProperty) + .visibleIf(templateProvidersLoadingProperty) + label("") + .bindText(templateProvidersText2Property) + .visibleIf(templateProvidersLoadingProperty) - val provideTemplate = Consumer<() -> Collection> { provider -> - loadTemplatesInBackground(provider, taskParentComponent) + templateProviderPlaceholder = placeholder() } templateProviderProperty.afterChange { templateProvider -> templatePropertyPlaceholder.component = null availableTemplates = emptyList() - availableTemplatesSegmentedButton.items(availableTemplates) - templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) + templateProviderPlaceholder.component = + templateProvider.setupUi(context, propertyGraph, ::loadTemplatesInBackground) } builder.row(MCDevBundle("creator.ui.custom.templates.label")) { @@ -123,14 +147,16 @@ class CustomPlatformStep( segmentedButton(emptyList(), LoadedTemplate::label, LoadedTemplate::tooltip) .bind(selectedTemplateProperty) }.visibleIf( - availableTemplatesProperty.transform { it.size > 1 } or - templateProviderProperty.transform { it is RecentTemplatesProvider } + availableTemplatesProperty.transform { it.size > 1 } ) availableTemplatesProperty.afterChange { newTemplates -> availableTemplatesSegmentedButton.items(newTemplates) + // Force visiblity because the component might become hidden and not show up again + // when the segmented button switches between dropdown and buttons + availableTemplatesSegmentedButton.visible(true) templatePropertyPlaceholder.component = null - selectedTemplate = EmptyLoadedTemplate + selectedTemplate = newTemplates.firstOrNull() ?: EmptyLoadedTemplate } selectedTemplateProperty.afterChange { template -> @@ -138,24 +164,34 @@ class CustomPlatformStep( } builder.row { + templatePropertiesProcessIcon = + cell(AsyncProcessIcon("Templates loading")) + .visibleIf(templateLoadingProperty) + label(MCDevBundle("creator.step.generic.load_template.message")) + .visibleIf(templateLoadingProperty) + label("") + .bindText(templateLoadingTextProperty) + .visibleIf(templateLoadingProperty) + label("") + .bindText(templateLoadingText2Property) + .visibleIf(templateLoadingProperty) + noTemplatesAvailable = label(MCDevBundle("creator.step.generic.no_templates_available.message")) + .visible(false) + .apply { component.foreground = JBColor.RED } templatePropertyPlaceholder = placeholder().align(AlignX.FILL) }.topGap(TopGap.SMALL) - initTemplates(null) - - templateProviderPlaceholder.component = templateProvider.setupUi(context, propertyGraph, provideTemplate) + initTemplates() } - private fun initTemplates( - taskParentComponent: JComponent? - ) { + private fun initTemplates() { selectedTemplate = EmptyLoadedTemplate - val task = object : Task.Modal( + val task = object : Task.Backgroundable( context.project, - taskParentComponent, MCDevBundle("creator.step.generic.init_template_providers.message"), - false + true, + ALWAYS_BACKGROUND, ) { override fun run(indicator: ProgressIndicator) { @@ -163,45 +199,82 @@ class CustomPlatformStep( return } + application.invokeAndWait({ + ProgressManager.checkCanceled() + templateProvidersLoadingProperty.set(true) + VirtualFileManager.getInstance().syncRefresh() + }, getWizardModalityState()) + for (provider in templateProviders) { + ProgressManager.checkCanceled() indicator.text = provider.getLabel() runCatching { provider.init(indicator) } .getOrLogException(logger()) } + + ProgressManager.checkCanceled() + application.invokeAndWait({ + ProgressManager.checkCanceled() + templateProvidersLoadingProperty.set(false) + // Force refresh to trigger template loading + templateProviderProperty.set(templateProvider) + }, getWizardModalityState()) } } - ProgressManager.getInstance().run(task) + val indicator = CreatorProgressIndicator( + templateProvidersLoadingProperty, + templateProvidersTextProperty, + templateProvidersText2Property + ) + ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, indicator) } - private fun loadTemplatesInBackground( - provider: () -> Collection, - taskParentComponent: JComponent? - ) { + private fun loadTemplatesInBackground(provider: () -> Collection) { selectedTemplate = EmptyLoadedTemplate - val task = object : Task.WithResult, Exception>( + val task = object : Task.Backgroundable( context.project, - taskParentComponent, - MCDevBundle("creator.step.generic.init_template_providers.message"), - false + MCDevBundle("creator.step.generic.load_template.message"), + true, + ALWAYS_BACKGROUND, ) { - override fun compute(indicator: ProgressIndicator): Collection { + override fun run(indicator: ProgressIndicator) { if (project?.isDisposed == true) { - return emptyList() + return } - return runCatching { provider() } + application.invokeAndWait({ + ProgressManager.checkCanceled() + templateLoadingProperty.set(true) + VirtualFileManager.getInstance().syncRefresh() + }, getWizardModalityState()) + + ProgressManager.checkCanceled() + val newTemplates = runCatching { provider() } .getOrLogException(logger()) ?: emptyList() + + ProgressManager.checkCanceled() + application.invokeAndWait({ + ProgressManager.checkCanceled() + templateLoadingProperty.set(false) + noTemplatesAvailable.visible(newTemplates.isEmpty()) + availableTemplates = newTemplates + }, getWizardModalityState()) } } - val newTemplates = ProgressManager.getInstance().run(task) - availableTemplates = newTemplates - availableTemplatesSegmentedButton.items(newTemplates) - availableTemplatesSegmentedButton.selectedItem = newTemplates.firstOrNull() + templateLoadingIndicator?.cancel() + + val indicator = CreatorProgressIndicator( + templateLoadingProperty, + templateLoadingTextProperty, + templateLoadingText2Property + ) + templateLoadingIndicator = indicator + ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, indicator) } private fun createOptionsPanelInBackground(template: LoadedTemplate, placeholder: Placeholder) { @@ -412,4 +485,15 @@ class CustomPlatformStep( } } } + + private fun getWizardModalityState(): ModalityState { + val contentPanel = context.getUserData(AbstractWizard.KEY)?.contentPanel + + if (contentPanel == null) { + thisLogger().error("Wizard content panel is null, using default modality state") + return ModalityState.defaultModalityState() + } + + return ModalityState.stateForComponent(contentPanel) + } } diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 8480aa8c7..006b8e63d 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -28,7 +28,7 @@ import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.onError import com.intellij.ide.util.projectWizard.WizardContext - import com.intellij.openapi.diagnostic.ControlFlowException +import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.progress.ProgressIndicator diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index d0e318c7c..527d023d8 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -30,6 +30,7 @@ import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.readText import java.util.function.Consumer @@ -68,8 +69,8 @@ interface TemplateProvider { directory: VirtualFile, templates: MutableList = mutableListOf(), ): List { - directory.refresh(false, true) - for (child in directory.children) { + for (child in directory.children) { // TODO use visitor instead of loop + ProgressManager.checkCanceled() if (child.isDirectory) { findTemplates(child, templates) } else if (child.name.endsWith(".mcdev.template.json")) { @@ -98,9 +99,6 @@ interface TemplateProvider { descriptorFile: VirtualFile, tooltip: String? = null ): VfsLoadedTemplate? { - root.refresh(false, true) - descriptorFile.refresh(false, false) - var descriptor = Gson().fromJson(descriptorFile.readText()) if (descriptor.version != 1) { thisLogger().warn("Cannot handle template ${descriptorFile.path} of version ${descriptor.version}") @@ -118,7 +116,6 @@ interface TemplateProvider { if (descriptor.inherit != null) { val parent = root.findFileByRelativePath(descriptor.inherit!!) if (parent != null) { - parent.refresh(false, false) val parentDescriptor = Gson().fromJson(parent.readText()) val mergedProperties = parentDescriptor.properties.orEmpty() + descriptor.properties.orEmpty() val mergedFiles = parentDescriptor.files.orEmpty() + descriptor.files.orEmpty() diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 55c3febd6..d4b8eaf4c 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -71,7 +71,9 @@ creator.ui.generic_unfinished.message=Haven''t finished {0} creator.ui.create_minecraft_project=Create a new Minecraft project creator.step.generic.project_created.message=Your project is being created -creator.step.generic.init_template_providers.message=Initializing Templates +creator.step.generic.init_template_providers.message=Initializing templates +creator.step.generic.load_template.message=Loading templates +creator.step.generic.no_templates_available.message=There are no templates available creator.step.gradle.patch_gradle.description=Patching Gradle files creator.step.gradle.import_gradle.description=Importing Gradle project From e1ab3ae56a1a922b229367cd2b92600058c0fa1d Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 9 Jun 2024 21:00:11 +0200 Subject: [PATCH 076/118] Localization support --- build.gradle.kts | 4 +- .../buildsystem/AbstractBuildSystemStep.kt | 2 +- src/main/kotlin/creator/creator-utils.kt | 16 +++ .../custom/CreatorProgressIndicator.kt | 20 ++++ .../creator/custom/CustomPlatformStep.kt | 27 ++--- .../custom/ResourceBundleTranslator.kt | 47 ++++++++ .../creator/custom/TemplateDescriptor.kt | 19 +++- .../creator/custom/TemplateResourceBundle.kt | 32 ++++++ .../providers/BuiltInTemplateProvider.kt | 8 +- .../custom/providers/LocalTemplateProvider.kt | 10 +- .../providers/RecentTemplatesProvider.kt | 6 +- .../custom/providers/TemplateProvider.kt | 100 +++++++++++++++--- .../custom/providers/VfsLoadedTemplate.kt | 52 ++++----- .../custom/providers/ZipTemplateProvider.kt | 11 +- .../custom/types/BooleanCreatorProperty.kt | 13 ++- .../custom/types/ClassFqnCreatorProperty.kt | 2 +- .../creator/custom/types/CreatorProperty.kt | 2 +- .../custom/types/FabricVersionsProperty.kt | 21 ++-- .../custom/types/ForgeVersionsProperty.kt | 5 +- .../types/InlineStringListCreatorProperty.kt | 2 +- .../custom/types/IntegerCreatorProperty.kt | 2 +- .../custom/types/JdkCreatorProperty.kt | 2 +- .../creator/custom/types/LicenseProperty.kt | 2 +- .../types/MavenArtifactVersionProperty.kt | 2 +- .../custom/types/NeoForgeVersionsProperty.kt | 6 +- .../creator/custom/types/ParchmentProperty.kt | 2 +- .../types/SemanticVersionCreatorProperty.kt | 2 +- .../custom/types/SimpleCreatorProperty.kt | 11 +- .../custom/types/StringCreatorProperty.kt | 2 +- src/main/kotlin/creator/step/UseMixinsStep.kt | 2 +- src/main/kotlin/util/files.kt | 5 +- .../messages/MinecraftDevelopment.properties | 38 ++++++- .../MinecraftDevelopment_zh.properties | 4 +- 33 files changed, 364 insertions(+), 115 deletions(-) create mode 100644 src/main/kotlin/creator/custom/ResourceBundleTranslator.kt create mode 100644 src/main/kotlin/creator/custom/TemplateResourceBundle.kt diff --git a/build.gradle.kts b/build.gradle.kts index 1c4116d7b..48e4ab67b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -414,8 +414,8 @@ tasks.runIde { systemProperty("idea.debug.mode", "true") } // Set these properties to test different languages - // systemProperty("user.language", "en") - // systemProperty("user.country", "US") + systemProperty("user.language", "fr") + systemProperty("user.country", "FR") } tasks.buildSearchableOptions { diff --git a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt index 51614b1fb..887682753 100644 --- a/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt +++ b/src/main/kotlin/creator/buildsystem/AbstractBuildSystemStep.kt @@ -49,7 +49,7 @@ abstract class AbstractBuildSystemStep( override val self get() = this override val label - get() = MCDevBundle("creator.ui.build_system.label.generic") + get() = MCDevBundle("creator.ui.build_system.label") override fun initSteps(): LinkedHashMap { context.putUserData(PLATFORM_NAME_KEY, platformName) diff --git a/src/main/kotlin/creator/creator-utils.kt b/src/main/kotlin/creator/creator-utils.kt index 687793ddf..a1ab81512 100644 --- a/src/main/kotlin/creator/creator-utils.kt +++ b/src/main/kotlin/creator/creator-utils.kt @@ -26,11 +26,15 @@ import com.demonwav.mcdev.creator.step.LicenseStep import com.demonwav.mcdev.util.MinecraftTemplates import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.ide.starters.local.GeneratorTemplateFile +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.ide.wizard.AbstractNewProjectWizardStep +import com.intellij.ide.wizard.AbstractWizard import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.notification.Notification import com.intellij.notification.NotificationType +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.observable.properties.ObservableProperty import com.intellij.openapi.project.Project @@ -160,3 +164,15 @@ fun notifyCreatedProjectNotOpened() { NotificationType.ERROR, ).notify(null) } + +val WizardContext.modalityState: ModalityState + get() { + val contentPanel = this.getUserData(AbstractWizard.KEY)?.contentPanel + + if (contentPanel == null) { + thisLogger().error("Wizard content panel is null, using default modality state") + return ModalityState.defaultModalityState() + } + + return ModalityState.stateForComponent(contentPanel) + } diff --git a/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt b/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt index 682310be1..2bad9bf12 100644 --- a/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt +++ b/src/main/kotlin/creator/custom/CreatorProgressIndicator.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.intellij.openapi.observable.properties.GraphProperty diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index fe9ecafa5..c622b77ec 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -29,16 +29,15 @@ import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory import com.demonwav.mcdev.creator.custom.types.ExternalCreatorProperty +import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.util.toTypedArray import com.demonwav.mcdev.util.virtualFileOrError import com.intellij.codeInsight.actions.ReformatCodeProcessor import com.intellij.ide.projectView.ProjectView import com.intellij.ide.wizard.AbstractNewProjectWizardStep -import com.intellij.ide.wizard.AbstractWizard import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger @@ -203,7 +202,7 @@ class CustomPlatformStep( ProgressManager.checkCanceled() templateProvidersLoadingProperty.set(true) VirtualFileManager.getInstance().syncRefresh() - }, getWizardModalityState()) + }, context.modalityState) for (provider in templateProviders) { ProgressManager.checkCanceled() @@ -218,7 +217,7 @@ class CustomPlatformStep( templateProvidersLoadingProperty.set(false) // Force refresh to trigger template loading templateProviderProperty.set(templateProvider) - }, getWizardModalityState()) + }, context.modalityState) } } @@ -249,7 +248,7 @@ class CustomPlatformStep( ProgressManager.checkCanceled() templateLoadingProperty.set(true) VirtualFileManager.getInstance().syncRefresh() - }, getWizardModalityState()) + }, context.modalityState) ProgressManager.checkCanceled() val newTemplates = runCatching { provider() } @@ -262,7 +261,7 @@ class CustomPlatformStep( templateLoadingProperty.set(false) noTemplatesAvailable.visible(newTemplates.isEmpty()) availableTemplates = newTemplates - }, getWizardModalityState()) + }, context.modalityState) } } @@ -319,14 +318,15 @@ class CustomPlatformStep( .map { it.first } val factory = Consumer { panel -> + val label = descriptor.translatedLabel if (descriptor.collapsible == false) { - panel.group(descriptor.label) { + panel.group(label) { for (childFactory in childrenUiFactories) { childFactory.accept(this@group) } } } else { - val group = panel.collapsibleGroup(descriptor.label) { + val group = panel.collapsibleGroup(label) { for (childFactory in childrenUiFactories) { childFactory.accept(this@collapsibleGroup) } @@ -485,15 +485,4 @@ class CustomPlatformStep( } } } - - private fun getWizardModalityState(): ModalityState { - val contentPanel = context.getUserData(AbstractWizard.KEY)?.contentPanel - - if (contentPanel == null) { - thisLogger().error("Wizard content panel is null, using default modality state") - return ModalityState.defaultModalityState() - } - - return ModalityState.stateForComponent(contentPanel) - } } diff --git a/src/main/kotlin/creator/custom/ResourceBundleTranslator.kt b/src/main/kotlin/creator/custom/ResourceBundleTranslator.kt new file mode 100644 index 000000000..c90077f45 --- /dev/null +++ b/src/main/kotlin/creator/custom/ResourceBundleTranslator.kt @@ -0,0 +1,47 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.asset.MCDevBundle +import com.intellij.openapi.util.text.StringUtil +import java.util.MissingResourceException +import java.util.ResourceBundle +import org.jetbrains.annotations.Nls +import org.jetbrains.annotations.NonNls + +abstract class ResourceBundleTranslator { + + abstract val bundle: ResourceBundle? + + fun translate(key: @NonNls String): @Nls String { + return translateOrNull(key) ?: StringUtil.escapeMnemonics(key) + } + + fun translateOrNull(key: @NonNls String): @Nls String? { + if (bundle != null) { + try { + return bundle!!.getString(key) + } catch (_: MissingResourceException) { + } + } + return MCDevBundle.messageOrNull(key) + } +} diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 0d26c5021..638d628d4 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -20,6 +20,8 @@ package com.demonwav.mcdev.creator.custom +import java.util.ResourceBundle + data class TemplateDescriptor( val version: Int, val label: String? = null, @@ -28,12 +30,15 @@ data class TemplateDescriptor( val properties: List? = null, val files: List? = null, val finalizers: List>? = null, -) +) : ResourceBundleTranslator() { + @Transient + override var bundle: ResourceBundle? = null +} data class TemplatePropertyDescriptor( val name: String, val type: String, - val label: String, + val label: String? = null, val order: Int? = null, val options: Any? = null, val limit: Int? = null, @@ -51,7 +56,15 @@ data class TemplatePropertyDescriptor( val inheritFrom: String? = null, val parameters: Map? = null, val validator: Any? = null -) +) : ResourceBundleTranslator() { + @Transient + override var bundle: ResourceBundle? = null + + val translatedLabel: String + get() = translate(label ?: "creator.ui.${name.lowercase()}.label") + val translatedWarning: String? + get() = translateOrNull(label ?: "creator.ui.${name.lowercase()}.warning") ?: warning +} data class PropertyDerivation( val parents: List? = null, diff --git a/src/main/kotlin/creator/custom/TemplateResourceBundle.kt b/src/main/kotlin/creator/custom/TemplateResourceBundle.kt new file mode 100644 index 000000000..ecb4d4bf0 --- /dev/null +++ b/src/main/kotlin/creator/custom/TemplateResourceBundle.kt @@ -0,0 +1,32 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom + +import java.io.Reader +import java.util.PropertyResourceBundle +import java.util.ResourceBundle + +class TemplateResourceBundle(val reader: Reader, parent: ResourceBundle?) : PropertyResourceBundle(reader) { + + init { + this.parent = parent + } +} diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt index 006b8e63d..7801cfdf5 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.virtualFile @@ -28,6 +29,7 @@ import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.onError import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.PropertyGraph @@ -92,11 +94,13 @@ class BuiltInTemplateProvider : TemplateProvider { provideTemplate: Consumer<() -> Collection> ): JComponent? { provideTemplate.accept { - builtinTemplatesPath.virtualFile?.let(TemplateProvider::findTemplates) ?: emptyList() + builtinTemplatesPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } + ?: emptyList() } return null } - override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) + override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = + TemplateProvider.deserializeAndLoadVfs(element, modalityState) } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index 3c75151aa..6ecad90f3 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -21,8 +21,10 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.util.virtualFile import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage @@ -51,8 +53,9 @@ class LocalTemplateProvider : TemplateProvider { val pathProperty = propertyGraph.property("").apply { afterChange { path -> provideTemplate.accept { - val root = Path.of(path.trim()).absolute() - root.virtualFile?.let(TemplateProvider::findTemplates) ?: emptyList() + val rootPath = Path.of(path.trim()).absolute() + rootPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } + ?: emptyList() } } bindStorage("${this@LocalTemplateProvider.javaClass.name}.path") @@ -82,5 +85,6 @@ class LocalTemplateProvider : TemplateProvider { } } - override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) + override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = + TemplateProvider.deserializeAndLoadVfs(element, modalityState) } diff --git a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt index 2ac5897c4..5507df580 100644 --- a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt @@ -21,7 +21,9 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.RecentProjectTemplates +import com.demonwav.mcdev.creator.modalityState import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.observable.properties.PropertyGraph import java.util.function.Consumer import javax.swing.JComponent @@ -37,12 +39,12 @@ class RecentTemplatesProvider : TemplateProvider { ): JComponent? { provideTemplate.accept { RecentProjectTemplates.instance.state.templates.mapNotNull { (provider, element) -> - TemplateProvider.get(provider)?.deserializeAndLoad(element) + TemplateProvider.get(provider)?.deserializeAndLoad(element, context.modalityState) } } return null } - override fun deserializeAndLoad(element: String): LoadedTemplate = + override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate = throw UnsupportedOperationException("The recent templates provider is not supposed to deserialize nor load") } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 527d023d8..b01cf2540 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -21,9 +21,13 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor +import com.demonwav.mcdev.creator.custom.TemplateResourceBundle import com.demonwav.mcdev.util.fromJson +import com.demonwav.mcdev.util.refreshSync import com.google.gson.Gson +import com.intellij.DynamicBundle import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger @@ -33,6 +37,7 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.readText +import java.util.ResourceBundle import java.util.function.Consumer import javax.swing.JComponent @@ -53,7 +58,7 @@ interface TemplateProvider { provideTemplate: Consumer<() -> Collection> ): JComponent? - fun deserializeAndLoad(element: String): LoadedTemplate? + fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? companion object { @@ -66,16 +71,22 @@ interface TemplateProvider { fun getAll(): Collection = EP_NAME.extensionList fun findTemplates( - directory: VirtualFile, + modalityState: ModalityState, + repoRoot: VirtualFile, + directory: VirtualFile = repoRoot, templates: MutableList = mutableListOf(), + bundle: ResourceBundle? = loadMessagesBundle(modalityState, repoRoot) ): List { + directory.refreshSync(modalityState) for (child in directory.children) { // TODO use visitor instead of loop ProgressManager.checkCanceled() if (child.isDirectory) { - findTemplates(child, templates) + findTemplates(modalityState, repoRoot, child, templates, bundle) } else if (child.name.endsWith(".mcdev.template.json")) { try { - createVfsLoadedTemplate(directory, child)?.let(templates::add) + createVfsLoadedTemplate(modalityState, repoRoot, directory, child, bundle = bundle)?.let( + templates::add + ) } catch (t: Throwable) { if (t is ControlFlowException) { throw t @@ -94,11 +105,65 @@ interface TemplateProvider { return templates } + fun loadMessagesBundle(modalityState: ModalityState, repoRoot: VirtualFile): ResourceBundle? = try { + val locale = DynamicBundle.getLocale() + // Simplified bundle resolution, but covers all the most common cases + val baseBundle = doLoadMessageBundle( + repoRoot.findChild("messages.properties"), + modalityState, + null + ) + val languageBundle = doLoadMessageBundle( + repoRoot.findChild("messages_${locale.language}.properties"), + modalityState, + baseBundle + ) + doLoadMessageBundle( + repoRoot.findChild("messages_${locale.language}_${locale.country}.properties"), + modalityState, + languageBundle + ) + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + + thisLogger().error("Failed to load resource bundle of template repository ${repoRoot.path}", t) + null + } + + private fun doLoadMessageBundle( + file: VirtualFile?, + modalityState: ModalityState, + parent: ResourceBundle? + ): ResourceBundle? { + if (file == null) { + return parent + } + + try { + file.refreshSync(modalityState) + return file.inputStream.reader().use { TemplateResourceBundle(it, parent) } + } catch (t: Throwable) { + if (t is ControlFlowException) { + return parent + } + + thisLogger().error("Failed to load resource bundle ${file.path}", t) + } + + return parent + } + fun createVfsLoadedTemplate( - root: VirtualFile, + modalityState: ModalityState, + repoRoot: VirtualFile, + templateRoot: VirtualFile, descriptorFile: VirtualFile, - tooltip: String? = null + tooltip: String? = null, + bundle: ResourceBundle? = null ): VfsLoadedTemplate? { + descriptorFile.refreshSync(modalityState) var descriptor = Gson().fromJson(descriptorFile.readText()) if (descriptor.version != 1) { thisLogger().warn("Cannot handle template ${descriptorFile.path} of version ${descriptor.version}") @@ -109,13 +174,18 @@ interface TemplateProvider { return null } - val label = descriptor.label + descriptor.bundle = bundle + + val labelKey = descriptor.label ?: descriptorFile.name.removeSuffix(".mcdev.template.json").takeIf(String::isNotBlank) - ?: root.presentableName + ?: templateRoot.presentableName + val label = + descriptor.translateOrNull("platform.${labelKey.lowercase()}.label") ?: descriptor.translate(labelKey) if (descriptor.inherit != null) { - val parent = root.findFileByRelativePath(descriptor.inherit!!) + val parent = templateRoot.findFileByRelativePath(descriptor.inherit!!) if (parent != null) { + parent.refresh(false, false) val parentDescriptor = Gson().fromJson(parent.readText()) val mergedProperties = parentDescriptor.properties.orEmpty() + descriptor.properties.orEmpty() val mergedFiles = parentDescriptor.files.orEmpty() + descriptor.files.orEmpty() @@ -127,12 +197,18 @@ interface TemplateProvider { } } - return VfsLoadedTemplate(root, descriptorFile, label, tooltip, descriptor, true) + if (bundle != null) { + descriptor.properties?.forEach { property -> + property.bundle = bundle + } + } + + return VfsLoadedTemplate(repoRoot, templateRoot, descriptorFile, label, tooltip, descriptor, true) } - fun deserializeAndLoadVfs(element: String): LoadedTemplate? { + fun deserializeAndLoadVfs(element: String, modalityState: ModalityState): LoadedTemplate? { val serialized = Gson().fromJson(element) - return serialized.load() + return serialized.load(modalityState) } } } diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index 20be38eb1..ec2f18495 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -22,19 +22,16 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.google.gson.Gson -import com.intellij.openapi.diagnostic.getOrLogException -import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.vfs.JarFileSystem import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.readText -import com.intellij.util.xmlb.XmlSerializer import java.io.FileNotFoundException -import org.jdom.Element class VfsLoadedTemplate( - val root: VirtualFile, + val repoRoot: VirtualFile, + val templateRoot: VirtualFile, val descriptorFile: VirtualFile, override val label: String, override val tooltip: String? = null, @@ -43,48 +40,45 @@ class VfsLoadedTemplate( ) : LoadedTemplate { override fun loadTemplateContents(path: String): String? { - root.refresh(false, true) - val virtualFile = root.findFileByRelativePath(path) - ?: throw FileNotFoundException("Could not find file $path in template root ${root.path}") + templateRoot.refresh(false, true) + val virtualFile = templateRoot.findFileByRelativePath(path) + ?: throw FileNotFoundException("Could not find file $path in template root ${templateRoot.path}") virtualFile.refresh(false, false) return virtualFile.readText() } data class Serialized( - val root: String, + val repoRoot: String, + val templateRoot: String, val descriptorPath: String ) { - fun load(): LoadedTemplate? { - val fs = if (root.contains(JarFileSystem.JAR_SEPARATOR)) { + fun load(modalityState: ModalityState): LoadedTemplate? { + val fs = if (repoRoot.contains(JarFileSystem.JAR_SEPARATOR)) { JarFileSystem.getInstance() } else { LocalFileSystem.getInstance() } - val rootFile = fs.refreshAndFindFileByPath(root) + val repoRoot = fs.refreshAndFindFileByPath(repoRoot) + ?: return null + val templateRoot = fs.refreshAndFindFileByPath(templateRoot) ?: return null val descriptorFile = fs.refreshAndFindFileByPath(descriptorPath) ?: return null val tooltip = descriptorFile.path - return TemplateProvider.createVfsLoadedTemplate(rootFile, descriptorFile, tooltip) + val bundle = TemplateProvider.loadMessagesBundle(modalityState, repoRoot) + return TemplateProvider.createVfsLoadedTemplate( + modalityState, + repoRoot, + templateRoot, + descriptorFile, + tooltip, + bundle + ) } } override fun serialize(): String? { - return Gson().toJson(Serialized(root.path, descriptorFile.path)) - } - - companion object { - - fun deserialize(element: Element): VfsLoadedTemplate? = runCatching { - val serialized = XmlSerializer.deserialize(element, Serialized::class.java) - - val rootFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(serialized.root) - ?: return null - val descriptorFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(serialized.descriptorPath) - ?: return null - - TemplateProvider.createVfsLoadedTemplate(rootFile, descriptorFile) - }.getOrLogException(logger()) + return Gson().toJson(Serialized(repoRoot.path, templateRoot.path, descriptorFile.path)) } } diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 5cb9c9e5d..47f7fa87a 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -21,7 +21,9 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.modalityState import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage @@ -50,7 +52,7 @@ class ZipTemplateProvider : TemplateProvider { val pathProperty = propertyGraph.property("").apply { afterChange { path -> provideTemplate.accept { - loadTemplatesFrom(path) + loadTemplatesFrom(path, context.modalityState) } } bindStorage("${this@ZipTemplateProvider.javaClass.name}.path") @@ -77,16 +79,17 @@ class ZipTemplateProvider : TemplateProvider { } } - override fun deserializeAndLoad(element: String): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element) + override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = + TemplateProvider.deserializeAndLoadVfs(element, modalityState) companion object { - fun loadTemplatesFrom(archivePath: String): List { + fun loadTemplatesFrom(archivePath: String, modalityState: ModalityState): List { val archiveRoot = archivePath + JarFileSystem.JAR_SEPARATOR val fs = JarFileSystem.getInstance() val rootFile = fs.refreshAndFindFileByPath(archiveRoot) ?: return emptyList() - return TemplateProvider.findTemplates(rootFile) + return TemplateProvider.findTemplates(modalityState, rootFile) } } } diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index b400a4f05..a32819c11 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -26,6 +26,7 @@ import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.content.AlertIcon import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindSelected class BooleanCreatorProperty( @@ -41,12 +42,16 @@ class BooleanCreatorProperty( override fun deserialize(string: String): Boolean = string.toBoolean() override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { - if (descriptor.warning != null) { - icon(AlertIcon(AllIcons.General.Warning)).comment(descriptor.warning) + val label = descriptor.translatedLabel + panel.row(label) { + val warning = descriptor.translatedWarning + if (warning != null) { + icon(AlertIcon(AllIcons.General.Warning)) + .gap(RightGap.SMALL) + .comment(descriptor.translate(warning)) } - this.checkBox(descriptor.label.removeSuffix(":")) + this.checkBox(label.removeSuffix(":").trim()) .bindSelected(graphProperty) .enabled(descriptor.editable != false) }.visible(descriptor.hidden != true) diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 0657e723f..049ade355 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -48,7 +48,7 @@ class ClassFqnCreatorProperty( override fun deserialize(string: String): ClassFqn = ClassFqn(string) override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { this.textField().bindText(this@ClassFqnCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .textValidation(BuiltinValidations.validClassFqn) diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 5735b98fb..69a5f6d75 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -149,7 +149,7 @@ abstract class CreatorProperty( } protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { - row(descriptor.label) { + row(descriptor.translatedLabel) { comboBox(options) .bindItem(graphProp) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index 3da0a4938..9cf785e2c 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor @@ -135,7 +136,7 @@ class FabricVersionsProperty( } override fun buildUi(panel: Panel, context: WizardContext) { - panel.row("Minecraft Version:") { + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionModel) .bindItem(mcVersionProperty) .validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(graph)) @@ -143,11 +144,11 @@ class FabricVersionsProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - checkBox("Show snapshots") + checkBox(MCDevBundle("creator.ui.show_snapshots.label")) .bindSelected(showMcSnapshotsProperty) }.enabled(descriptor.editable != false) - panel.row("Loom Version:") { + panel.row(MCDevBundle("creator.ui.loom_version.label")) { comboBox(loomVersionModel) .bindItem(loomVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) @@ -155,7 +156,7 @@ class FabricVersionsProperty( .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) - panel.row("Loader Version:") { + panel.row(MCDevBundle("creator.ui.loader_version.label")) { comboBox(loaderVersionModel) .bindItem(loaderVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) @@ -163,7 +164,7 @@ class FabricVersionsProperty( .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) - panel.row("Yarn Version:") { + panel.row(MCDevBundle("creator.ui.yarn_version.label")) { comboBox(yarnVersionModel) .bindItem(yarnVersionProperty) .enabledIf(useOfficialMappingsProperty.not()) @@ -171,15 +172,15 @@ class FabricVersionsProperty( .validationOnApply(BuiltinValidations.nonEmptyYarnVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - checkBox("Use official mappings") + checkBox(MCDevBundle("creator.ui.use_official_mappings.label")) .bindSelected(useOfficialMappingsProperty) - label("Unable to match Yarn versions to Minecraft version") + label(MCDevBundle("creator.ui.warn.no_yarn_to_mc_match")) .visibleIf(yarnHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) - panel.row("FabricApi Version:") { + panel.row(MCDevBundle("creator.ui.fabricapi_version.label")) { comboBox(fabricApiVersionModel) .bindItem(fabricApiVersionProperty) .enabledIf(useFabricApiVersionProperty) @@ -187,9 +188,9 @@ class FabricVersionsProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - checkBox("Use FabricApi") + checkBox(MCDevBundle("creator.ui.use_fabricapi.label")) .bindSelected(useFabricApiVersionProperty) - label("Unable to match API versions to Minecraft version") + label(MCDevBundle("creator.ui.warn.no_fabricapi_to_mc_match")) .visibleIf(fabricApiHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt index 2129202f9..7784524b4 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor @@ -32,6 +33,7 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application import javax.swing.DefaultComboBoxModel @@ -86,13 +88,14 @@ class ForgeVersionsProperty( } override fun buildUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionsModel) .bindItem(mcVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } + label(MCDevBundle("creator.ui.forge_version.label")).gap(RightGap.SMALL) comboBox(forgeVersionsModel) .bindItem(forgeVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt index f0ef1a122..3cac2509e 100644 --- a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -45,7 +45,7 @@ class InlineStringListCreatorProperty( .run(::StringList) override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { this.textField().bindText(this@InlineStringListCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index a272ac253..4b4162086 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -46,7 +46,7 @@ class IntegerCreatorProperty( override fun deserialize(string: String): Int = string.toIntOrNull() ?: 0 override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { this.intTextField().bindIntText(graphProperty) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index f7772bd14..a2ef6377b 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -47,7 +47,7 @@ class JdkCreatorProperty( CreatorJdk(ProjectJdkTable.getInstance().allJdks.find { it.homePath == string }) override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { val sdkProperty = graphProperty.transform(CreatorJdk::sdk, ::CreatorJdk) jdkComboBox = this.jdkComboBoxWithPreference(context, sdkProperty, descriptor.name).component diff --git a/src/main/kotlin/creator/custom/types/LicenseProperty.kt b/src/main/kotlin/creator/custom/types/LicenseProperty.kt index 85b516601..ed4a2e4aa 100644 --- a/src/main/kotlin/creator/custom/types/LicenseProperty.kt +++ b/src/main/kotlin/creator/custom/types/LicenseProperty.kt @@ -51,7 +51,7 @@ class LicenseProperty( LicenseData(string, License.byId(string)?.toString() ?: string, ZonedDateTime.now().year.toString()) override fun buildUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { val model = EnumComboBoxModel(License::class.java) val licenseEnumProperty = graphProperty.transform( { License.byId(it.id) ?: License.entries.first() }, diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt index 89cd90368..c0955ed84 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt @@ -67,7 +67,7 @@ class MavenArtifactVersionProperty( } override fun buildUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { val combobox = comboBox(versionsProperty.get()) .bindItem(graphProperty) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt index 7a8e64da6..81193c8fb 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor @@ -33,6 +34,7 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application import javax.swing.DefaultComboBoxModel @@ -89,19 +91,21 @@ class NeoForgeVersionsProperty( } override fun buildUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionsModel) .bindItem(mcVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } + label(MCDevBundle("creator.ui.neoforge_version.label")).gap(RightGap.SMALL) comboBox(nfVersionsModel) .bindItem(nfVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } + label(MCDevBundle("creator.ui.neogradle_version.label")).gap(RightGap.SMALL) comboBox(ngVersionsModel) .bindItem(ngVersionProperty) .validationOnInput(BuiltinValidations.nonEmptyVersion) diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index 1e7ac3914..b303e1b17 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -92,7 +92,7 @@ class ParchmentProperty( } override fun buildUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { checkBox("Use Parchment") .bindSelected(useParchmentProperty) diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 207ca502f..329bf06a1 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -45,7 +45,7 @@ open class SemanticVersionCreatorProperty( SemanticVersion.tryParse(string) ?: SemanticVersion(emptyList()) override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { this.textField().bindText(this@SemanticVersionCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_SHORT) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 1a06df0d0..65d75c1e5 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -41,8 +41,13 @@ abstract class SimpleCreatorProperty( private fun makeOptionsList(): Map? { val map = when (val options = descriptor.options) { - is Map<*, *> -> options.mapValues { it.value.toString() } - is Iterable<*> -> options.associateWithTo(linkedMapOf()) { it.toString() } + is Map<*, *> -> options.mapValues { descriptor.translate(it.value.toString()) } + is Iterable<*> -> options.associateWithTo(linkedMapOf()) { + val optionKey = it.toString() + descriptor.translateOrNull("creator.ui.${descriptor.name.lowercase()}.option.${optionKey.lowercase()}") + ?: optionKey + } + else -> null } @@ -74,7 +79,7 @@ abstract class SimpleCreatorProperty( override fun buildUi(panel: Panel, context: WizardContext) { if (isDropdown) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { if (descriptor.forceDropdown == true) { comboBox(options!!.keys, DropdownAutoRenderer()) .bindItem(graphProperty) diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 07052162d..97d302cc4 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -71,7 +71,7 @@ class StringCreatorProperty( } override fun buildSimpleUi(panel: Panel, context: WizardContext) { - panel.row(descriptor.label) { + panel.row(descriptor.translatedLabel) { val textField = textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/step/UseMixinsStep.kt b/src/main/kotlin/creator/step/UseMixinsStep.kt index e5b9b1c1b..c328712b5 100644 --- a/src/main/kotlin/creator/step/UseMixinsStep.kt +++ b/src/main/kotlin/creator/step/UseMixinsStep.kt @@ -36,7 +36,7 @@ class UseMixinsStep(parent: NewProjectWizardStep) : AbstractNewProjectWizardStep override fun setupUI(builder: Panel) { with(builder) { - row(MCDevBundle("creator.ui.mixins.label")) { + row(MCDevBundle("creator.ui.use_mixins.label")) { checkBox("") .bindSelected(useMixinsProperty) } diff --git a/src/main/kotlin/util/files.kt b/src/main/kotlin/util/files.kt index a1847d967..b69d49d7b 100644 --- a/src/main/kotlin/util/files.kt +++ b/src/main/kotlin/util/files.kt @@ -20,9 +20,11 @@ package com.demonwav.mcdev.util +import com.intellij.openapi.application.ModalityState import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.newvfs.RefreshQueue import java.io.File import java.io.IOException import java.nio.file.Path @@ -57,6 +59,7 @@ val VirtualFile.mcPath: String? operator fun Manifest.get(attribute: String): String? = mainAttributes.getValue(attribute) operator fun Manifest.get(attribute: Attributes.Name): String? = mainAttributes.getValue(attribute) -fun VirtualFile.refreshFs(): VirtualFile { +fun VirtualFile.refreshSync(modalityState: ModalityState): VirtualFile { + RefreshQueue.getInstance().refresh(false, this.isDirectory, null, modalityState, this) return this.parent.findOrCreateChildData(this, this.name) } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index d4b8eaf4c..fc3769cb3 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -18,7 +18,7 @@ # along with this program. If not, see . # -creator.ui.build_system.label.generic=Build System: +creator.ui.build_system.label=Build System: creator.ui.build_system.label.gradle=Gradle creator.ui.build_system.label.maven=Maven @@ -44,9 +44,11 @@ creator.ui.custom.archive.dialog.description=Select the ZIP file containing the creator.ui.license.label=License: creator.ui.main_class.label=Main Class: -creator.ui.mc_version.label=Minecraft Version: -creator.ui.mod_name.label=Mod Name: -creator.ui.plugin_name.label=Plugin Name: +creator.ui.mc_version.label=Minecraft &Version: +creator.ui.mod_name.label=Mod &Name: +creator.ui.mod_id.label=Mod &ID: +creator.ui.plugin_name.label=Plugin &Name: +creator.ui.plugin_id.label=Plugin &ID: creator.ui.description.label=Description: creator.ui.authors.label=Authors: creator.ui.website.label=Website: @@ -54,13 +56,39 @@ creator.ui.repository.label=Repository: creator.ui.issue_tracker.label=Issue Tracker: creator.ui.update_url.label=Update URL: creator.ui.depend.label=Depend: +creator.ui.log_prefix.label=Log Prefix: +creator.ui.load_at.label=Load At: +creator.ui.load_at.option.startup=Startup: +creator.ui.load_at.option.postworld=Post World: creator.ui.soft_depend.label=Soft Depend: -creator.ui.mixins.label=Use Mixins: +creator.ui.use_mixins.label=Use &Mixins: +creator.ui.java_version.label=Java Version: +creator.ui.jdk.label=JDK: +creator.ui.optional_settings.label=Optional Settings creator.ui.parchment.label=Parchment: creator.ui.parchment.include.label=Include: creator.ui.parchment.include.old_mc.label=Older Minecraft versions creator.ui.parchment.include.snapshots.label=Snapshot versions creator.ui.parchment.no_version.message=No versions of Parchment matching your configuration +creator.ui.mod_environment.label=Environment: +creator.ui.mod_environment.option.*=Both +creator.ui.mod_environment.option.client=Client +creator.ui.mod_environment.option.server=Server +creator.ui.forge_version.label=Forge: +creator.ui.neoforge_version.label=NeoForge: +creator.ui.neogradle_version.label=NeoGradle: +creator.ui.show_snapshots.label=Show snapshots: +creator.ui.loom_version.label=Loom Version: +creator.ui.loader_version.label=Loader Version: +creator.ui.yarn_version.label=Yarn Version: +creator.ui.use_official_mappings.label=Use official mappings +creator.ui.fabricapi_version.label=FabricApi Version: +creator.ui.use_fabricapi.label=Use FabricApi +creator.ui.spongeapi_version.label=Sponge Version: +creator.ui.velocity_version.label=Velocity Version: + +creator.ui.warn.no_yarn_to_mc_match=Unable to match Yarn versions to Minecraft version +creator.ui.warn.no_fabricapi_to_mc_match=Unable to match API versions to Minecraft version creator.ui.outdated.message=Is the Minecraft project wizard outdated? \ Create an issue on the MinecraftDev issue tracker. diff --git a/src/main/resources/messages/MinecraftDevelopment_zh.properties b/src/main/resources/messages/MinecraftDevelopment_zh.properties index 5cac19f5d..c1f1e223e 100644 --- a/src/main/resources/messages/MinecraftDevelopment_zh.properties +++ b/src/main/resources/messages/MinecraftDevelopment_zh.properties @@ -18,7 +18,7 @@ # along with this program. If not, see . # -creator.ui.build_system.label.generic=构建系统: +creator.ui.build_system.label=构建系统: creator.ui.build_system.label.gradle=Gradle creator.ui.build_system.label.maven=Maven @@ -45,7 +45,7 @@ creator.ui.issue_tracker.label=Issue Tracker: creator.ui.update_url.label=更新 URL: creator.ui.depend.label=依赖: creator.ui.soft_depend.label=软依赖: -creator.ui.mixins.label=使用 Mixins: +creator.ui.use_mixins.label=使用 Mixins: creator.ui.parchment.label=Parchment: creator.ui.parchment.include.label=Include: creator.ui.parchment.include.old_mc.label=更旧的 Minecraft 版本 From 7387383bb25ce797b6cfcc2c17a904f43e3ccc0f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 11 Jun 2024 19:45:38 +0200 Subject: [PATCH 077/118] Display validation and unhandled errors to user --- .../creator/custom/CustomPlatformStep.kt | 67 ++++++++--- .../custom/TemplateValidationReporter.kt | 106 ++++++++++++++++++ .../custom/finalizers/CreatorFinalizer.kt | 49 ++++++-- .../finalizers/RunGradleTasksFinalizer.kt | 17 ++- .../types/ArchitecturyVersionsProperty.kt | 5 +- .../BuildSystemCoordinatesCreatorProperty.kt | 5 +- .../creator/custom/types/CreatorProperty.kt | 24 ++-- .../custom/types/ExternalCreatorProperty.kt | 3 +- .../custom/types/FabricVersionsProperty.kt | 5 +- .../custom/types/ForgeVersionsProperty.kt | 5 +- .../custom/types/NeoForgeVersionsProperty.kt | 5 +- .../creator/custom/types/ParchmentProperty.kt | 5 +- .../custom/types/StringCreatorProperty.kt | 32 +++--- .../messages/MinecraftDevelopment.properties | 5 + 14 files changed, 271 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/creator/custom/TemplateValidationReporter.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index c622b77ec..2e7f77f35 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -108,6 +108,8 @@ class CustomPlatformStep( lateinit var noTemplatesAvailable: Cell var templateLoadingIndicator: ProgressIndicator? = null + private var hasTemplateErrors: Boolean = true + private var properties = mutableMapOf>() override fun setupUI(builder: Panel) { @@ -145,6 +147,9 @@ class CustomPlatformStep( availableTemplatesSegmentedButton = segmentedButton(emptyList(), LoadedTemplate::label, LoadedTemplate::tooltip) .bind(selectedTemplateProperty) + .validation { + addApplyRule("", condition = ::hasTemplateErrors) + } }.visibleIf( availableTemplatesProperty.transform { it.size > 1 } ) @@ -289,31 +294,69 @@ class CustomPlatformStep( properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) placeholder.component = panel { - for (uiFactory in setupTemplate(template)) { - uiFactory.accept(this) + val reporter = TemplateValidationReporterImpl() + val uiFactories = setupTemplate(template, reporter) + if (uiFactories.isEmpty() && !reporter.hasErrors) { + row { + label(MCDevBundle("creator.ui.warn.no_properties")) + .component.foreground = JBColor.YELLOW + } + } else { + reporter.display(this) + + if (!reporter.hasErrors) { + for (uiFactory in uiFactories) { + uiFactory.accept(this) + } + } } } } - private fun setupTemplate(template: LoadedTemplate): List> { + private fun setupTemplate( + template: LoadedTemplate, + reporter: TemplateValidationReporterImpl + ): List> { return try { - template.descriptor.properties.orEmpty() - .mapNotNull { setupProperty(it) } + val properties = template.descriptor.properties.orEmpty() + .mapNotNull { + reporter.subject = it.name + setupProperty(it, reporter) + } .sortedBy { (_, order) -> order } .map { it.first } + + val finalizers = template.descriptor.finalizers + if (finalizers != null) { + CreatorFinalizer.validateAll(reporter, finalizers) + } + + properties } catch (t: Throwable) { if (t is ControlFlowException) { throw t } - thisLogger().error(t) + + thisLogger().error( + "Unexpected error during template setup", + t, + template.label, + template.descriptor.toString() + ) + emptyList() + } finally { + reporter.subject = null } } - private fun setupProperty(descriptor: TemplatePropertyDescriptor): Pair, Int>? { + private fun setupProperty( + descriptor: TemplatePropertyDescriptor, + reporter: TemplateValidationReporter + ): Pair, Int>? { if (!descriptor.groupProperties.isNullOrEmpty()) { val childrenUiFactories = descriptor.groupProperties - .mapNotNull(::setupProperty) + .mapNotNull { setupProperty(it, reporter) } .sortedBy { (_, order) -> order } .map { it.first } @@ -341,17 +384,15 @@ class CustomPlatformStep( } if (descriptor.name in properties.keys) { - thisLogger().error("Duplicate property name ${descriptor.name}") - return null + reporter.fatal("Duplicate property name ${descriptor.name}") } val prop = CreatorPropertyFactory.createFromType(descriptor.type, descriptor, propertyGraph, properties) if (prop == null) { - thisLogger().error("Unknown template property type ${descriptor.type}") - return null + reporter.fatal("Unknown template property type ${descriptor.type}") } - prop.setupProperty() + prop.setupProperty(reporter) properties[descriptor.name] = prop diff --git a/src/main/kotlin/creator/custom/TemplateValidationReporter.kt b/src/main/kotlin/creator/custom/TemplateValidationReporter.kt new file mode 100644 index 000000000..b953eb16e --- /dev/null +++ b/src/main/kotlin/creator/custom/TemplateValidationReporter.kt @@ -0,0 +1,106 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.asset.MCDevBundle +import com.intellij.ui.JBColor +import com.intellij.ui.dsl.builder.Panel + +interface TemplateValidationReporter { + + fun warn(message: String) + + fun error(message: String) + + fun fatal(message: String, cause: Throwable? = null): Nothing +} + +class TemplateValidationReporterImpl : TemplateValidationReporter { + + private val validationItems: MutableMap> = linkedMapOf() + var hasErrors = false + private set + var hasWarns = false + private set + + var subject: String? = null + + override fun warn(message: String) { + check(subject != null) { "No subject is being validated" } + hasWarns = true + validationItems.getOrPut(subject!!, ::mutableListOf).add(TemplateValidationItem.Warn(message)) + } + + override fun error(message: String) { + check(subject != null) { "No subject is being validated" } + hasErrors = true + validationItems.getOrPut(subject!!, ::mutableListOf).add(TemplateValidationItem.Error(message)) + } + + override fun fatal(message: String, cause: Throwable?): Nothing { + error("Fatal validation error: $message") + throw TemplateValidationException(message, cause) + } + + fun display(panel: Panel) { + if (!hasErrors && !hasWarns) { + return + } + + panel.row { + when { + hasWarns && hasErrors -> label(MCDevBundle("creator.ui.error.template_warns_and_errors")).apply { + component.foreground = JBColor.RED + } + + hasWarns -> label(MCDevBundle("creator.ui.error.template_warns")).apply { + component.foreground = JBColor.YELLOW + } + + hasErrors -> label(MCDevBundle("creator.ui.error.template_errors")).apply { + component.foreground = JBColor.RED + } + } + } + + for ((subjectName, items) in validationItems) { + panel.row { + label("$subjectName:") + } + + panel.indent { + for (item in items) { + row { + label(item.message).component.foreground = item.color + } + } + } + } + } +} + +class TemplateValidationException(message: String?, cause: Throwable? = null) : Exception(message, cause) + +private sealed class TemplateValidationItem(val message: String, val color: JBColor) { + + class Warn(message: String) : TemplateValidationItem(message, JBColor.YELLOW) + class Error(message: String) : TemplateValidationItem(message, JBColor.RED) +} diff --git a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt index 1f20aa339..458c048d2 100644 --- a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt @@ -21,6 +21,8 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.TemplateValidationReporterImpl import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName @@ -33,6 +35,8 @@ import com.intellij.util.xmlb.annotations.Attribute interface CreatorFinalizer { + fun validate(reporter: TemplateValidationReporter, properties: Map) = Unit + fun execute(project: Project, properties: Map, templateProperties: Map) companion object { @@ -40,14 +44,42 @@ interface CreatorFinalizer { ExtensionPointName.create("com.demonwav.minecraft-dev.creatorFinalizer") private val COLLECTOR = KeyedExtensionCollector(EP_NAME) - fun executeAll(project: Project, finalizers: List>, templateProperties: Map) { - for (properties in finalizers) { + fun validateAll( + reporter: TemplateValidationReporterImpl, + finalizers: List>, + ) { + for ((index, properties) in finalizers.withIndex()) { + reporter.subject = "Finalizer #$index" + val type = properties["type"] as? String if (type == null) { - thisLogger().warn("Missing finalizer 'type' value") - continue + reporter.error("Missing required 'type' value") } + val condition = properties["condition"] + if (condition != null && condition !is String) { + reporter.error("'condition' must be a string") + } + + if (type != null) { + val finalizer = COLLECTOR.findSingle(type) + if (finalizer == null) { + reporter.error("Unknown finalizer of type $type") + } else { + try { + finalizer.validate(reporter, properties) + } catch (t: Throwable) { + reporter.error("Unexpected error during finalizer validation: ${t.message}") + thisLogger().error("Unexpected error during finalizer validation", t) + } + } + } + } + } + + fun executeAll(project: Project, finalizers: List>, templateProperties: Map) { + for ((index, properties) in finalizers.withIndex()) { + val type = properties["type"] as String val condition = properties["condition"] as? String if (condition != null && !TemplateEvaluator.condition(templateProperties, condition).getOrElse { false } @@ -55,19 +87,14 @@ interface CreatorFinalizer { continue } - val finalizer = COLLECTOR.findSingle(type) - if (finalizer == null) { - thisLogger().warn("Unknown finalizer $type") - continue - } - + val finalizer = COLLECTOR.findSingle(type)!! try { finalizer.execute(project, properties, templateProperties) } catch (t: Throwable) { if (t is ControlFlowException) { throw t } - thisLogger().error("Unhandled exception in finalizer $type", t) + thisLogger().error("Unhandled exception in finalizer #$index ($type)", t) } } } diff --git a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt index bef854b34..419a7f84f 100644 --- a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.finalizers +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.util.runGradleTaskAndWait import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.Project @@ -27,12 +28,22 @@ import com.intellij.openapi.project.guessProjectDir class RunGradleTasksFinalizer : CreatorFinalizer { - override fun execute(project: Project, properties: Map, templateProperties: Map) { + override fun validate( + reporter: TemplateValidationReporter, + properties: Map + ) { @Suppress("UNCHECKED_CAST") val tasks = properties["tasks"] as? List - ?: return + if (tasks == null) { + reporter.warn("Missing list of 'tasks' to execute") + } + } + + override fun execute(project: Project, properties: Map, templateProperties: Map) { + @Suppress("UNCHECKED_CAST") + val tasks = properties["tasks"] as List val projectDir = project.guessProjectDir()?.toNioPath() - ?: return + ?: return thisLogger().error("Could not find project dir") thisLogger().info("tasks = $tasks projectDir = $projectDir") runGradleTaskAndWait(project, projectDir) { settings -> diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt index 6ad0d36f7..0e2aafefc 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.ArchitecturyVersionsModel import com.demonwav.mcdev.platform.architectury.ArchitecturyVersion import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions @@ -255,8 +256,8 @@ class ArchitecturyVersionsProperty( }.enabled(descriptor.editable != false) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) var previousMcVersion: SemanticVersion? = null mcVersionProperty.afterChange { mcVersion -> diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index 0a8d2708f..0bf9467b6 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty @@ -77,8 +78,8 @@ class BuildSystemCoordinatesCreatorProperty( return BuildSystemCoordinates(groupId, artifactId, version) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) val projectNameProperty = properties["PROJECT_NAME"]?.graphProperty if (projectNameProperty != null) { diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 69a5f6d75..7bf9fe033 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.thisLogger @@ -95,19 +96,28 @@ abstract class CreatorProperty( abstract fun buildUi(panel: Panel, context: WizardContext) - open fun setupProperty() { + /** + * Prepares everything this property needs, like calling [GraphProperty]'s [GraphProperty.afterChange] and + * [GraphProperty.dependsOn] on this property or other properties declared before this one. + * + * [properties] contains all the properties declared in the descriptor + * up to this one, forward references are not permitted. + * + * This is also where you should validate the [descriptor] values you want to use, and report all validation errors + * or warnings through the [reporter], use [TemplateValidationReporter.fatal] if the error is a show-stopper and + * the validation cannot even proceed further. + */ + open fun setupProperty(reporter: TemplateValidationReporter) { if (descriptor.remember != false && descriptor.derives == null) { toStringProperty(graphProperty).bindStorage(makeStorageKey()) } if (descriptor.derives != null) { val parents = descriptor.derives.parents - ?: throw RuntimeException("No parents specified in derivation of property '${descriptor.name}'") + ?: return reporter.error("No parents specified in derivation") for (parent in parents) { if (!properties.containsKey(parent)) { - throw RuntimeException( - "Unknown parent property '$parent' in derivation of property '${descriptor.name}'" - ) + return reporter.error("Unknown parent property '$parent' in derivation") } } @@ -126,9 +136,7 @@ abstract class CreatorProperty( if (descriptor.inheritFrom != null) { val parentProperty = properties[descriptor.inheritFrom] - ?: throw RuntimeException( - "Unknown parent property '${descriptor.inheritFrom}' in derivation of property '${descriptor.name}'" - ) + ?: return reporter.error("Unknown parent property '${descriptor.inheritFrom}' in derivation") @Suppress("UNCHECKED_CAST") graphProperty.set(parentProperty.graphProperty.get() as T) diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index 999a0770e..7787b93bc 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph @@ -33,7 +34,7 @@ class ExternalCreatorProperty( descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", default = ""), ) : CreatorProperty(descriptor, graph, properties) { - override fun setupProperty() = Unit + override fun setupProperty(reporter: TemplateValidationReporter) = Unit override fun createDefaultValue(raw: Any?): T = throw UnsupportedOperationException("Unsupported for external properties") diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt index 9cf785e2c..74364cadf 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.FabricVersionsModel import com.demonwav.mcdev.platform.fabric.util.FabricApiVersions import com.demonwav.mcdev.platform.fabric.util.FabricVersions @@ -196,8 +197,8 @@ class FabricVersionsProperty( }.enabled(descriptor.editable != false) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) showMcSnapshotsProperty.afterChange { updateMcVersionsList() } diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt index 7784524b4..056b44431 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.ForgeVersions import com.demonwav.mcdev.platform.forge.version.ForgeVersion import com.demonwav.mcdev.util.SemanticVersion @@ -104,8 +105,8 @@ class ForgeVersionsProperty( }.enabled(descriptor.editable != false) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) mcVersionProperty.afterChange { mcVersion -> if (mcVersion == previousMcVersion) { diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt index 81193c8fb..bedd8521b 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt @@ -24,6 +24,7 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion import com.demonwav.mcdev.platform.neoforge.version.NeoGradleVersion @@ -114,8 +115,8 @@ class NeoForgeVersionsProperty( }.enabled(descriptor.editable != false) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) mcVersionProperty.afterChange { mcVersion -> if (mcVersion == previousMcVersion) { diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt index b303e1b17..d88b55e21 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentProperty.kt @@ -22,6 +22,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.ParchmentVersion import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion import com.demonwav.mcdev.creator.custom.model.ParchmentVersions import com.demonwav.mcdev.util.SemanticVersion @@ -118,8 +119,8 @@ class ParchmentProperty( }.enabled(descriptor.editable != false) } - override fun setupProperty() { - super.setupProperty() + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) val platformMcVersionPropertyName = descriptor.parameters?.get("minecraftVersionProperty") as? String val platformMcVersionProperty = properties[platformMcVersionPropertyName] diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 97d302cc4..8338baae3 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -23,9 +23,8 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.diagnostic.ControlFlowException -import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.COLUMNS_LARGE @@ -40,6 +39,8 @@ class StringCreatorProperty( properties: Map> ) : SimpleCreatorProperty(graph, descriptor, properties) { + private var validationRegex: Regex? = null + override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" override fun serialize(value: String): String = value @@ -70,23 +71,26 @@ class StringCreatorProperty( return sanitized } + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) + + val regexString = descriptor.validator as? String + if (regexString != null) { + try { + validationRegex = regexString.toRegex() + } catch (t: Throwable) { + reporter.error("Invalid validator regex: '$regexString': ${t.message}") + } + } + } + override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.translatedLabel) { val textField = textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) - try { - val regexString = descriptor.validator as? String - if (regexString != null) { - val regex = regexString.toRegex() - textField.textValidation(BuiltinValidations.byRegex(regex)) - } - } catch (t: Throwable) { - if (t is ControlFlowException) { - throw t - } - logger() - .error("Failed to create validator for property ${descriptor.name}", t) + if (validationRegex != null) { + textField.textValidation(BuiltinValidations.byRegex(validationRegex!!)) } }.visible(descriptor.hidden != true) } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index fc3769cb3..8652d7749 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -42,6 +42,11 @@ creator.ui.custom.path.dialog.description=Select the root directory of the templ creator.ui.custom.archive.dialog.title=Template Archive creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template +creator.ui.warn.no_properties=This template has no properties +creator.ui.error.template_warns_and_errors=This template contains warnings and errors: +creator.ui.error.template_warns=This template contains warnings: +creator.ui.error.template_errors=This template contains errors: + creator.ui.license.label=License: creator.ui.main_class.label=Main Class: creator.ui.mc_version.label=Minecraft &Version: From a23e85cd1cf5f52dca994672834aeb696a739b28 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 11 Jun 2024 21:23:41 +0200 Subject: [PATCH 078/118] Split templates into groups --- .../creator/custom/CustomPlatformStep.kt | 26 +++++++++++++++++-- .../creator/custom/TemplateDescriptor.kt | 4 +++ .../messages/MinecraftDevelopment.properties | 6 ++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 2e7f77f35..b96a44270 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -88,10 +88,15 @@ class CustomPlatformStep( val templateProviderProperty = propertyGraph.property(templateProviders.first()) var templateProvider by templateProviderProperty + val availableGroupsProperty = propertyGraph.property>(emptyList()) + var availableGroups by availableGroupsProperty val availableTemplatesProperty = propertyGraph.property>(emptyList()) var availableTemplates by availableTemplatesProperty + lateinit var availableGroupsSegmentedButton: SegmentedButton lateinit var availableTemplatesSegmentedButton: SegmentedButton + val selectedGroupProperty = propertyGraph.property("") + var selectedGroup by selectedGroupProperty val selectedTemplateProperty = propertyGraph.property(EmptyLoadedTemplate) var selectedTemplate by selectedTemplateProperty @@ -143,6 +148,14 @@ class CustomPlatformStep( templateProvider.setupUi(context, propertyGraph, ::loadTemplatesInBackground) } + builder.row(MCDevBundle("creator.ui.custom.groups.label")) { + availableGroupsSegmentedButton = + segmentedButton(emptyList(), String::toString) + .bind(selectedGroupProperty) + }.visibleIf( + availableGroupsProperty.transform { it.size > 1 } + ) + builder.row(MCDevBundle("creator.ui.custom.templates.label")) { availableTemplatesSegmentedButton = segmentedButton(emptyList(), LoadedTemplate::label, LoadedTemplate::tooltip) @@ -155,12 +168,21 @@ class CustomPlatformStep( ) availableTemplatesProperty.afterChange { newTemplates -> - availableTemplatesSegmentedButton.items(newTemplates) + val groups = newTemplates.mapTo(linkedSetOf()) { it.descriptor.translatedGroup } + availableGroupsSegmentedButton.items(groups) + // availableGroupsSegmentedButton.visible(groups.size > 1) + availableGroups = groups + selectedGroup = groups.firstOrNull() ?: "empty" + } + + selectedGroupProperty.afterChange { group -> + val templates = availableTemplates.filter { it.descriptor.translatedGroup == group } + availableTemplatesSegmentedButton.items(templates) // Force visiblity because the component might become hidden and not show up again // when the segmented button switches between dropdown and buttons availableTemplatesSegmentedButton.visible(true) templatePropertyPlaceholder.component = null - selectedTemplate = newTemplates.firstOrNull() ?: EmptyLoadedTemplate + selectedTemplate = templates.firstOrNull() ?: EmptyLoadedTemplate } selectedTemplateProperty.afterChange { template -> diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 638d628d4..1b5c895e6 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -25,6 +25,7 @@ import java.util.ResourceBundle data class TemplateDescriptor( val version: Int, val label: String? = null, + val group: String? = null, val inherit: String? = null, val hidden: Boolean? = null, val properties: List? = null, @@ -33,6 +34,9 @@ data class TemplateDescriptor( ) : ResourceBundleTranslator() { @Transient override var bundle: ResourceBundle? = null + + val translatedGroup: String + get() = translate("creator.ui.group.${(group ?: "default").lowercase()}.label") } data class TemplatePropertyDescriptor( diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 8652d7749..236cb8c84 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -29,12 +29,16 @@ creator.ui.group.version=Version: creator.ui.platform.type.label=Platform Type: creator.ui.platform.label=Platform: -creator.ui.platform.custom.name=Custom creator.ui.platform.mod.name=Mod creator.ui.platform.plugin.name=Plugin +creator.ui.group.default.label=Default +creator.ui.group.mod.label=Mod +creator.ui.group.plugin.label=Plugin +creator.ui.group.proxy.label=Proxy creator.ui.custom.step.description=Creating project based on template... creator.ui.custom.provider.label=Template Provider: +creator.ui.custom.groups.label=Groups: creator.ui.custom.templates.label=Templates: creator.ui.custom.path.label=Templates Path: creator.ui.custom.path.dialog.title=Template Root From 01a3f01ef483f7af9912864a74960075bb4c360f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 14 Jun 2024 09:36:55 +0200 Subject: [PATCH 079/118] Bump templates --- templates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates b/templates index 3d60a1e2f..e238cceee 160000 --- a/templates +++ b/templates @@ -1 +1 @@ -Subproject commit 3d60a1e2f7ba5947eab45b33a36121e4e9c91b3e +Subproject commit e238cceeef0e3eba238e045c1c56b2cd26662a14 From 2523e1c251958a43cddd429de0734eb0b785a6fa Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 03:01:16 +0200 Subject: [PATCH 080/118] Add user-configurable repositories instead of raw providers --- src/main/kotlin/MinecraftConfigurable.kt | 115 ++++++++++- src/main/kotlin/MinecraftSettings.kt | 35 +++- .../creator/custom/BuiltinValidations.kt | 2 + .../creator/custom/CustomPlatformStep.kt | 40 ++-- .../providers/BuiltInTemplateProvider.kt | 106 ----------- .../custom/providers/LocalTemplateProvider.kt | 37 ++-- .../providers/RecentTemplatesProvider.kt | 50 ----- .../providers/RemoteTemplateProvider.kt | 179 ++++++++++++++++++ .../custom/providers/TemplateProvider.kt | 56 ++++-- .../custom/providers/ZipTemplateProvider.kt | 32 ++-- src/main/kotlin/util/files.kt | 4 +- src/main/resources/META-INF/plugin.xml | 12 +- .../messages/MinecraftDevelopment.properties | 14 +- 13 files changed, 435 insertions(+), 247 deletions(-) delete mode 100644 src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt delete mode 100644 src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt create mode 100644 src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index 5614a4ac5..b08cd7357 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -22,20 +22,36 @@ package com.demonwav.mcdev import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.asset.PlatformAssets +import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.update.ConfigurePluginUpdatesDialog import com.intellij.ide.projectView.ProjectView import com.intellij.openapi.options.Configurable import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.ui.DialogPanel +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.util.NlsContexts +import com.intellij.psi.impl.cache.impl.id.IdDataConsumer +import com.intellij.ui.ComboBoxTableCellRenderer import com.intellij.ui.EnumComboBoxModel +import com.intellij.ui.ToolbarDecorator import com.intellij.ui.components.Label +import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.BottomGap +import com.intellij.ui.dsl.builder.MutableProperty +import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.table.TableView import com.intellij.util.IconUtil +import com.intellij.util.ListWithSelection +import com.intellij.util.ui.ColumnInfo +import com.intellij.util.ui.ListTableModel +import com.intellij.util.ui.table.ComboBoxTableCellEditor import javax.swing.JComponent +import javax.swing.table.TableCellEditor +import javax.swing.table.TableCellRenderer import org.jetbrains.annotations.Nls class MinecraftConfigurable : Configurable { @@ -91,12 +107,103 @@ class MinecraftConfigurable : Configurable { } } + val nameColumn = object : + ColumnInfo( + MCDevBundle("minecraft.settings.creator.repos.column.name") + ) { + + override fun valueOf(item: MinecraftSettings.TemplateRepo?): String? { + return item?.name + } + + override fun setValue(item: MinecraftSettings.TemplateRepo?, value: String?) { + item?.name = value ?: MCDevBundle("minecraft.settings.creator.repo.default_name") + } + + override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true + } + + val providerColumn = object : ColumnInfo( + MCDevBundle("minecraft.settings.creator.repos.column.provider") + ) { + + override fun valueOf(item: MinecraftSettings.TemplateRepo?): ListWithSelection? { + val providers = TemplateProvider.getAllKeys() + val list = ListWithSelection(providers) + list.select(item?.provider?.takeUnless { it.isBlank() }) + + return list + } + + override fun setValue(item: MinecraftSettings.TemplateRepo?, value: Any?) { + item?.provider = value as? String ?: "local" + } + + override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true + + override fun getRenderer(item: MinecraftSettings.TemplateRepo?): TableCellRenderer? { + return ComboBoxTableCellRenderer.INSTANCE + } + + override fun getEditor(item: MinecraftSettings.TemplateRepo?): TableCellEditor? { + return ComboBoxTableCellEditor.INSTANCE + } + } + + val model = object : ListTableModel(nameColumn, providerColumn) { + override fun addRow() { + val defaultName = MCDevBundle("minecraft.settings.creator.repo.default_name") + addRow(MinecraftSettings.TemplateRepo(defaultName, "local", "")) + } + } group(MCDevBundle("minecraft.settings.creator")) { + row(MCDevBundle("minecraft.settings.creator.repos")) {} + row { - checkBox(MCDevBundle("minecraft.settings.creator.auto_update_builtin_templates")) - .comment(MCDevBundle("minecraft.settings.creator.auto_update_builtin_templates.comment")) - .bindSelected(settings::isAutoUpdateBuiltinTemplate) - } + val table = TableView() + table.setShowGrid(true) + table.model = model + table.tableHeader.reorderingAllowed = false + + val decoratedTable = ToolbarDecorator.createDecorator(table) + .setEditActionUpdater { + val selectedRepo = table.selection.firstOrNull() + ?: return@setEditActionUpdater false + val provider = TemplateProvider.get(selectedRepo.provider) + ?: return@setEditActionUpdater false + return@setEditActionUpdater provider.hasConfig + } + .setEditAction { + val selectedRepo = table.selection.firstOrNull() + ?: return@setEditAction + val provider = TemplateProvider.get(selectedRepo.provider) + ?: return@setEditAction + val dataConsumer = { data: String -> selectedRepo.data = data } + val configPanel = provider.setupConfigUi(selectedRepo.data, dataConsumer) + ?: return@setEditAction + + val dialog = object : DialogWrapper(null) { + init { + init() + } + + override fun createCenterPanel(): JComponent = configPanel + } + dialog.title = MCDevBundle("minecraft.settings.creator.repo_config.title", selectedRepo.name) + dialog.show() + } + .createPanel() + cell(decoratedTable) + .align(Align.FILL) + .bind( + { _ -> model.items }, + { _, repos -> model.items = repos; }, + MutableProperty( + { settings.creatorTemplateRepos.toMutableList() }, + { settings.creatorTemplateRepos = it } + ) + ) + }.resizableRow() } onApply { diff --git a/src/main/kotlin/MinecraftSettings.kt b/src/main/kotlin/MinecraftSettings.kt index ff2eb8628..2c916212f 100644 --- a/src/main/kotlin/MinecraftSettings.kt +++ b/src/main/kotlin/MinecraftSettings.kt @@ -20,11 +20,15 @@ package com.demonwav.mcdev +import com.demonwav.mcdev.asset.MCDevBundle import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.editor.markup.EffectType +import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.util.xmlb.annotations.Tag +import com.intellij.util.xmlb.annotations.Text @State(name = "MinecraftSettings", storages = [Storage("minecraft_dev.xml")]) class MinecraftSettings : PersistentStateComponent { @@ -38,9 +42,27 @@ class MinecraftSettings : PersistentStateComponent { var isShadowAnnotationsSameLine: Boolean = true, - var autoUpdateBuiltinTemplate: Boolean = true, + var creatorTemplateRepos: List = emptyList(), ) + @Tag("repo") + data class TemplateRepo( + @get:Attribute("name") + var name: String, + @get:Attribute("provider") + var provider: String, + @get:Text + var data: String + ) { + constructor() : this("", "", "") + + companion object { + + fun makeBuiltinRepo() = + TemplateRepo(MCDevBundle("minecraft.settings.creator.repo.builtin_name"), "remote", "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip\ntrue") + } + } + private var state = State() override fun getState(): State { @@ -49,6 +71,9 @@ class MinecraftSettings : PersistentStateComponent { override fun loadState(state: State) { this.state = state + if (state.creatorTemplateRepos.isEmpty()) { + state.creatorTemplateRepos = listOf() + } } // State mappings @@ -88,10 +113,10 @@ class MinecraftSettings : PersistentStateComponent { state.isShadowAnnotationsSameLine = shadowAnnotationsSameLine } - var isAutoUpdateBuiltinTemplate: Boolean - get() = state.autoUpdateBuiltinTemplate - set(autoUpdateBuiltinTemplate) { - state.autoUpdateBuiltinTemplate = autoUpdateBuiltinTemplate + var creatorTemplateRepos: List + get() = state.creatorTemplateRepos.map { it.copy() } + set(creatorTemplateRepos) { + state.creatorTemplateRepos = creatorTemplateRepos.map { it.copy() } } enum class UnderlineType(private val regular: String, val effectType: EffectType) { diff --git a/src/main/kotlin/creator/custom/BuiltinValidations.kt b/src/main/kotlin/creator/custom/BuiltinValidations.kt index 8d2ebca56..b57d6610b 100644 --- a/src/main/kotlin/creator/custom/BuiltinValidations.kt +++ b/src/main/kotlin/creator/custom/BuiltinValidations.kt @@ -30,6 +30,8 @@ import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.text.StringUtil object BuiltinValidations { + val nonBlank = validationErrorIf(MCDevBundle("creator.validation.blank")) { it.isBlank() } + val validVersion = validationErrorIf(MCDevBundle("creator.validation.semantic_version")) { SemanticVersion.tryParse(it) == null } diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index b96a44270..e00f697b1 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -20,11 +20,11 @@ package com.demonwav.mcdev.creator.custom +import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.finalizers.CreatorFinalizer import com.demonwav.mcdev.creator.custom.providers.EmptyLoadedTemplate import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate -import com.demonwav.mcdev.creator.custom.providers.RecentTemplatesProvider import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.demonwav.mcdev.creator.custom.types.CreatorPropertyFactory @@ -83,10 +83,12 @@ class CustomPlatformStep( parent: NewProjectWizardStep, ) : AbstractNewProjectWizardStep(parent) { - val templateProviders = TemplateProvider.getAll() + val templateRepos = MinecraftSettings.instance.creatorTemplateRepos - val templateProviderProperty = propertyGraph.property(templateProviders.first()) - var templateProvider by templateProviderProperty + val templateRepoProperty = propertyGraph.property( + templateRepos.firstOrNull() ?: MinecraftSettings.TemplateRepo.makeBuiltinRepo() + ) + var templateRepo by templateRepoProperty val availableGroupsProperty = propertyGraph.property>(emptyList()) var availableGroups by availableGroupsProperty @@ -104,7 +106,6 @@ class CustomPlatformStep( val templateProvidersTextProperty = propertyGraph.property("") val templateProvidersText2Property = propertyGraph.property("") lateinit var templateProvidersProcessIcon: Cell - lateinit var templateProviderPlaceholder: Placeholder val templateLoadingProperty = propertyGraph.property(true) val templateLoadingTextProperty = propertyGraph.property("") @@ -120,9 +121,9 @@ class CustomPlatformStep( override fun setupUI(builder: Panel) { lateinit var templatePropertyPlaceholder: Placeholder - builder.row(MCDevBundle("creator.ui.custom.provider.label")) { - segmentedButton(templateProviders, TemplateProvider::getLabel, TemplateProvider::getTooltip) - .bind(templateProviderProperty) + builder.row(MCDevBundle("creator.ui.custom.repos.label")) { + segmentedButton(templateRepos, { it.name }) + .bind(templateRepoProperty) } builder.row { @@ -137,15 +138,15 @@ class CustomPlatformStep( label("") .bindText(templateProvidersText2Property) .visibleIf(templateProvidersLoadingProperty) - - templateProviderPlaceholder = placeholder() } - templateProviderProperty.afterChange { templateProvider -> + templateRepoProperty.afterChange { templateRepo -> templatePropertyPlaceholder.component = null availableTemplates = emptyList() - templateProviderPlaceholder.component = - templateProvider.setupUi(context, propertyGraph, ::loadTemplatesInBackground) + loadTemplatesInBackground { + val provider = TemplateProvider.get(templateRepo.provider) + provider?.loadTemplates(context, templateRepo).orEmpty() + } } builder.row(MCDevBundle("creator.ui.custom.groups.label")) { @@ -231,10 +232,12 @@ class CustomPlatformStep( VirtualFileManager.getInstance().syncRefresh() }, context.modalityState) - for (provider in templateProviders) { + for ((providerKey, repos) in templateRepos.groupBy { it.provider }) { ProgressManager.checkCanceled() + val provider = TemplateProvider.get(providerKey) + ?: continue indicator.text = provider.getLabel() - runCatching { provider.init(indicator) } + runCatching { provider.init(indicator, repos) } .getOrLogException(logger()) } @@ -243,7 +246,7 @@ class CustomPlatformStep( ProgressManager.checkCanceled() templateProvidersLoadingProperty.set(false) // Force refresh to trigger template loading - templateProviderProperty.set(templateProvider) + templateRepoProperty.set(templateRepo) }, context.modalityState) } } @@ -324,6 +327,7 @@ class CustomPlatformStep( .component.foreground = JBColor.YELLOW } } else { + hasTemplateErrors = reporter.hasErrors reporter.display(this) if (!reporter.hasErrors) { @@ -433,10 +437,6 @@ class CustomPlatformStep( return } - if (templateProvider !is RecentTemplatesProvider) { - RecentProjectTemplates.instance.addNewTemplate(templateProvider.javaClass.name, template) - } - val projectPath = context.projectDirectory val templateProperties = collectTemplateProperties() thisLogger().debug("Template properties: $templateProperties") diff --git a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt deleted file mode 100644 index 7801cfdf5..000000000 --- a/src/main/kotlin/creator/custom/providers/BuiltInTemplateProvider.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2024 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.creator.custom.providers - -import com.demonwav.mcdev.MinecraftSettings -import com.demonwav.mcdev.creator.modalityState -import com.demonwav.mcdev.creator.selectProxy -import com.demonwav.mcdev.update.PluginUtil -import com.demonwav.mcdev.util.virtualFile -import com.github.kittinunf.fuel.core.FuelManager -import com.github.kittinunf.result.getOrNull -import com.github.kittinunf.result.onError -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.application.ModalityState -import com.intellij.openapi.diagnostic.ControlFlowException -import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.observable.properties.PropertyGraph -import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.util.io.FileUtil -import com.intellij.util.io.ZipUtil -import java.util.function.Consumer -import javax.swing.JComponent -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.moveTo -import kotlin.io.path.writeBytes - -class BuiltInTemplateProvider : TemplateProvider { - - private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") - private var updatedBuiltinTemplates = false - - override fun getLabel(): String = "Built In" - - override fun init(indicator: ProgressIndicator) { - if (!updatedBuiltinTemplates && MinecraftSettings.instance.isAutoUpdateBuiltinTemplate) { - indicator.text2 = "Updating builtin templates" - - val manager = FuelManager() - val url = "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip" - - manager.proxy = selectProxy(url) - - val (_, _, result) = manager.get(url) - .header("User-Agent", "github_org/minecraft-dev/${PluginUtil.pluginVersion}") - .header("Accepts", "application/json") - .timeout(10000) - .response() - - val data = result.onError { - thisLogger().warn("Could not fetch builtin templates update", it) - }.getOrNull() ?: return - - try { - val zipPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates.zip") - zipPath.writeBytes(data) - FileUtil.deleteRecursively(builtinTemplatesPath) - ZipUtil.extract(zipPath, builtinTemplatesPath, null) - for (child in builtinTemplatesPath.resolve("templates-main").listDirectoryEntries()) { - child.moveTo(builtinTemplatesPath.resolve(child.fileName)) - } - - updatedBuiltinTemplates = true - thisLogger().info("Builtin template update applied successfully") - } catch (t: Throwable) { - if (t is ControlFlowException) { - throw t - } - thisLogger().error("Failed to apply builtin templates update", t) - } - } - } - - override fun setupUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> Collection> - ): JComponent? { - provideTemplate.accept { - builtinTemplatesPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } - ?: emptyList() - } - - return null - } - - override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = - TemplateProvider.deserializeAndLoadVfs(element, modalityState) -} diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index 6ecad90f3..9990e00f4 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.providers +import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.util.virtualFile @@ -27,7 +28,6 @@ import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph -import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.ui.dsl.builder.AlignX @@ -37,7 +37,6 @@ import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation import java.nio.file.Path -import java.util.function.Consumer import javax.swing.JComponent import kotlin.io.path.absolute @@ -45,22 +44,20 @@ class LocalTemplateProvider : TemplateProvider { override fun getLabel(): String = "Local" - override fun setupUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> Collection> - ): JComponent { - val pathProperty = propertyGraph.property("").apply { - afterChange { path -> - provideTemplate.accept { - val rootPath = Path.of(path.trim()).absolute() - rootPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } - ?: emptyList() - } - } - bindStorage("${this@LocalTemplateProvider.javaClass.name}.path") - } + override val hasConfig: Boolean = true + override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + val rootPath = Path.of(repo.data.trim()).absolute() + return rootPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } + ?: emptyList() + } + + override fun setupConfigUi( + data: String, + dataSetter: (String) -> Unit + ): JComponent? { + val propertyGraph = PropertyGraph("LocalTemplateProvider config") + val pathProperty = propertyGraph.property(data) return panel { row(MCDevBundle("creator.ui.custom.path.label")) { val pathChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor().apply { @@ -68,7 +65,7 @@ class LocalTemplateProvider : TemplateProvider { } textFieldWithBrowseButton( MCDevBundle("creator.ui.custom.path.dialog.title"), - context.project, + null, pathChooserDescriptor ).align(AlignX.FILL) .columns(COLUMNS_LARGE) @@ -82,6 +79,10 @@ class LocalTemplateProvider : TemplateProvider { } ) } + + onApply { + dataSetter(pathProperty.get()) + } } } diff --git a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt b/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt deleted file mode 100644 index 5507df580..000000000 --- a/src/main/kotlin/creator/custom/providers/RecentTemplatesProvider.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2024 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.creator.custom.providers - -import com.demonwav.mcdev.creator.custom.RecentProjectTemplates -import com.demonwav.mcdev.creator.modalityState -import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.application.ModalityState -import com.intellij.openapi.observable.properties.PropertyGraph -import java.util.function.Consumer -import javax.swing.JComponent - -class RecentTemplatesProvider : TemplateProvider { - - override fun getLabel(): String = "Recent" - - override fun setupUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> Collection> - ): JComponent? { - provideTemplate.accept { - RecentProjectTemplates.instance.state.templates.mapNotNull { (provider, element) -> - TemplateProvider.get(provider)?.deserializeAndLoad(element, context.modalityState) - } - } - return null - } - - override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate = - throw UnsupportedOperationException("The recent templates provider is not supposed to deserialize nor load") -} diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt new file mode 100644 index 000000000..d6c016d5e --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -0,0 +1,179 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.BuiltinValidations +import com.demonwav.mcdev.creator.modalityState +import com.demonwav.mcdev.creator.selectProxy +import com.demonwav.mcdev.update.PluginUtil +import com.demonwav.mcdev.util.virtualFile +import com.github.kittinunf.fuel.core.FuelManager +import com.github.kittinunf.result.getOrNull +import com.github.kittinunf.result.onError +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.diagnostic.ControlFlowException +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.trim +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.util.io.FileUtil +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.textValidation +import com.intellij.util.io.ZipUtil +import com.intellij.util.io.createDirectories +import java.nio.file.Path +import javax.swing.JComponent +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.moveTo +import kotlin.io.path.writeBytes + +class RemoteTemplateProvider : TemplateProvider { + + private var updatedTemplates = mutableSetOf() + + override fun getLabel(): String = "Remote" + + override val hasConfig: Boolean = true + + override fun init(indicator: ProgressIndicator, repos: List) { + for (repo in repos) { + ProgressManager.checkCanceled() + val remote = RemoteTemplateRepo.deserialize(repo.data) + ?: continue + if (!remote.autoUpdate || remote.url in updatedTemplates) { + continue + } + + indicator.text2 = "Updating remote repository ${repo.name}" + + val manager = FuelManager() + manager.proxy = selectProxy(remote.url) + val (_, _, result) = manager.get(remote.url) + .header("User-Agent", "github_org/minecraft-dev/${PluginUtil.pluginVersion}") + .header("Accepts", "application/json") + .timeout(10000) + .response() + + val data = result.onError { + thisLogger().warn("Could not fetch remote templates repository update at ${remote.url}", it) + }.getOrNull() ?: return + + try { + val remoteTemplatesDir = remote.getDestination(repo.name) + remoteTemplatesDir.createDirectories() + val zipPath = remoteTemplatesDir.resolveSibling("${repo.name}.zip") + zipPath.writeBytes(data) + FileUtil.deleteRecursively(remoteTemplatesDir) + ZipUtil.extract(zipPath, remoteTemplatesDir, null) + + // Loose way to find out if the url is a github repo archive + // In such cases there is a single directory in the root directory of the zip + // We simply move all its children to the base directory so the rest of the system uses the correct + // root directory for this repository + val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/(.*?).zip".toRegex() + val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(remote.url) + if (githubRepoArchiveMatcher != null) { + val repoName = githubRepoArchiveMatcher.groupValues[2] + val branchName = githubRepoArchiveMatcher.groupValues[3] + for (child in remoteTemplatesDir.resolve("$repoName-$branchName").listDirectoryEntries()) { + child.moveTo(remoteTemplatesDir.resolve(child.fileName)) + } + } + + updatedTemplates.add(remote.url) + + thisLogger().info("Remote templates repository update applied successfully") + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + thisLogger().error("Failed to apply remote templates repository update", t, repo.toString()) + } + } + } + + override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + val remote = RemoteTemplateRepo.deserialize(repo.data) + ?: return emptyList() + return remote.getDestination(repo.name).virtualFile + ?.let { TemplateProvider.findTemplates(context.modalityState, it) } + ?: emptyList() + } + + override fun setupConfigUi( + data: String, + dataSetter: (String) -> Unit + ): JComponent? { + val propertyGraph = PropertyGraph("RemoteTemplateProvider config") + val defaultRepo = RemoteTemplateRepo.deserialize(data) + val urlProperty = propertyGraph.property(defaultRepo?.url ?: "").trim() + val autoUpdateProperty = propertyGraph.property(defaultRepo?.autoUpdate != false) + + return panel { + row(MCDevBundle("creator.ui.custom.remote.url.label")) { + textField() + .bindText(urlProperty) + .textValidation(BuiltinValidations.nonBlank) + } + + row { + checkBox(MCDevBundle("creator.ui.custom.remote.auto_update.label")) + .bindSelected(autoUpdateProperty) + } + + onApply { + val repo = RemoteTemplateRepo(urlProperty.get(), autoUpdateProperty.get()) + dataSetter(repo.serialize()) + } + } + } + + override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = + TemplateProvider.deserializeAndLoadVfs(element, modalityState) + + private data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) { + + fun getDestination(repoName: String): Path { + return PathManager.getSystemDir().resolve("mcdev-templates").resolve(repoName) + } + + fun serialize(): String = "$url\n$autoUpdate" + + companion object { + fun deserialize(data: String): RemoteTemplateRepo? { + val lines = data.lines() + if (lines.size < 2) { + return null + } + + val (url, autoUpdate) = lines + return RemoteTemplateRepo(url, autoUpdate.toBoolean()) + } + } + } +} diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index b01cf2540..bcf8702ca 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.creator.custom.providers +import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.custom.TemplateResourceBundle import com.demonwav.mcdev.util.fromJson @@ -32,43 +33,48 @@ import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName -import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.extensions.RequiredElement import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.util.KeyedExtensionCollector import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.readText +import com.intellij.serviceContainer.BaseKeyedLazyInstance +import com.intellij.util.KeyedLazyInstance +import com.intellij.util.xmlb.annotations.Attribute import java.util.ResourceBundle -import java.util.function.Consumer import javax.swing.JComponent /** - * Extensions responsible for creating a [TemplateDescriptor] based on whatever data it is provided in its [UI][setupUi]. + * Extensions responsible for creating a [TemplateDescriptor] based on whatever data it is provided in its configuration + * [UI][setupConfigUi]. */ interface TemplateProvider { fun getLabel(): String - fun getTooltip(): String? = null + val hasConfig: Boolean - fun init(indicator: ProgressIndicator) = Unit + fun init(indicator: ProgressIndicator, repos: List) = Unit - fun setupUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> Collection> - ): JComponent? + fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection + + fun setupConfigUi(data: String, dataSetter: (String) -> Unit): JComponent? fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? companion object { - private val EP_NAME = ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") + private val EP_NAME = + ExtensionPointName("com.demonwav.minecraft-dev.creatorTemplateProvider") + private val COLLECTOR = KeyedExtensionCollector(EP_NAME) - fun get(name: String): TemplateProvider? { - return getAll().find { it.javaClass.name == name } - } + fun get(key: String): TemplateProvider? = COLLECTOR.findSingle(key) + + fun getAllKeys() = EP_NAME.extensionList.map { it.key } - fun getAll(): Collection = EP_NAME.extensionList + fun getAll(): Collection = + EP_NAME.extensionList.mapNotNull { KeyedExtensionCollector.instantiate(it) } fun findTemplates( modalityState: ModalityState, @@ -77,7 +83,6 @@ interface TemplateProvider { templates: MutableList = mutableListOf(), bundle: ResourceBundle? = loadMessagesBundle(modalityState, repoRoot) ): List { - directory.refreshSync(modalityState) for (child in directory.children) { // TODO use visitor instead of loop ProgressManager.checkCanceled() if (child.isDirectory) { @@ -142,8 +147,8 @@ interface TemplateProvider { } try { - file.refreshSync(modalityState) - return file.inputStream.reader().use { TemplateResourceBundle(it, parent) } + return file.refreshSync(modalityState) + ?.inputStream?.reader()?.use { TemplateResourceBundle(it, parent) } } catch (t: Throwable) { if (t is ControlFlowException) { return parent @@ -212,3 +217,18 @@ interface TemplateProvider { } } } + +class TemplateProviderBean : BaseKeyedLazyInstance(), KeyedLazyInstance { + + @Attribute("key") + @RequiredElement + lateinit var name: String + + @Attribute("implementation") + @RequiredElement + lateinit var implementation: String + + override fun getKey(): String? = name + + override fun getImplementationClassName(): String? = implementation +} diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 47f7fa87a..531c2de6e 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -20,13 +20,13 @@ package com.demonwav.mcdev.creator.custom.providers +import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.modalityState import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph -import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.vfs.JarFileSystem import com.intellij.ui.dsl.builder.AlignX @@ -36,7 +36,6 @@ import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation import java.nio.file.Path -import java.util.function.Consumer import javax.swing.JComponent import kotlin.io.path.isRegularFile @@ -44,19 +43,18 @@ class ZipTemplateProvider : TemplateProvider { override fun getLabel(): String = "Archive" - override fun setupUi( - context: WizardContext, - propertyGraph: PropertyGraph, - provideTemplate: Consumer<() -> Collection> + override val hasConfig: Boolean = true + + override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + return loadTemplatesFrom(repo.data, context.modalityState) + } + + override fun setupConfigUi( + data: String, + dataSetter: (String) -> Unit ): JComponent { - val pathProperty = propertyGraph.property("").apply { - afterChange { path -> - provideTemplate.accept { - loadTemplatesFrom(path, context.modalityState) - } - } - bindStorage("${this@ZipTemplateProvider.javaClass.name}.path") - } + val propertyGraph = PropertyGraph("ZipTemplateProvider config") + val pathProperty = propertyGraph.property(data) return panel { row(MCDevBundle("creator.ui.custom.path.label")) { @@ -65,7 +63,7 @@ class ZipTemplateProvider : TemplateProvider { .apply { description = MCDevBundle("creator.ui.custom.archive.dialog.description") } textFieldWithBrowseButton( MCDevBundle("creator.ui.custom.archive.dialog.title"), - context.project, + null, pathChooserDescriptor ).align(AlignX.FILL) .columns(COLUMNS_LARGE) @@ -76,6 +74,10 @@ class ZipTemplateProvider : TemplateProvider { } ) } + + onApply { + dataSetter(pathProperty.get()) + } } } diff --git a/src/main/kotlin/util/files.kt b/src/main/kotlin/util/files.kt index b69d49d7b..a00fd6c21 100644 --- a/src/main/kotlin/util/files.kt +++ b/src/main/kotlin/util/files.kt @@ -59,7 +59,7 @@ val VirtualFile.mcPath: String? operator fun Manifest.get(attribute: String): String? = mainAttributes.getValue(attribute) operator fun Manifest.get(attribute: Attributes.Name): String? = mainAttributes.getValue(attribute) -fun VirtualFile.refreshSync(modalityState: ModalityState): VirtualFile { +fun VirtualFile.refreshSync(modalityState: ModalityState): VirtualFile? { RefreshQueue.getInstance().refresh(false, this.isDirectory, null, modalityState, this) - return this.parent.findOrCreateChildData(this, this.name) + return this.parent?.findOrCreateChildData(this, this.name) } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index deeff5384..49c26a9ed 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -77,7 +77,9 @@ - + + + @@ -148,10 +150,9 @@ - - - - + + + @@ -222,7 +223,6 @@ - diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 236cb8c84..f6cd1fbdc 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -37,7 +37,7 @@ creator.ui.group.plugin.label=Plugin creator.ui.group.proxy.label=Proxy creator.ui.custom.step.description=Creating project based on template... -creator.ui.custom.provider.label=Template Provider: +creator.ui.custom.repos.label=Repositories: creator.ui.custom.groups.label=Groups: creator.ui.custom.templates.label=Templates: creator.ui.custom.path.label=Templates Path: @@ -45,6 +45,8 @@ creator.ui.custom.path.dialog.title=Template Root creator.ui.custom.path.dialog.description=Select the root directory of the template repository creator.ui.custom.archive.dialog.title=Template Archive creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template +creator.ui.custom.remote.url.label=Download URL: +creator.ui.custom.remote.auto_update.label=Auto update creator.ui.warn.no_properties=This template has no properties creator.ui.error.template_warns_and_errors=This template contains warnings and errors: @@ -125,6 +127,7 @@ creator.step.reformat.description=Reformatting files creator.validation.custom.path_not_a_directory=Path is not a directory creator.validation.custom.path_not_a_file=Path is not a file +creator.validation.blank=Must not be blank creator.validation.group_id_non_example=Group ID must be changed from "org.example" creator.validation.semantic_version=Version must be a valid semantic version creator.validation.class_fqn=Must be a valid class fully qualified name @@ -262,8 +265,13 @@ minecraft.settings.chat_color_underline_style=Chat color underline style: minecraft.settings.mixin=Mixin minecraft.settings.mixin.shadow_annotation_same_line=@Shadow annotations on same line minecraft.settings.creator=Creator -minecraft.settings.creator.auto_update_builtin_templates=Automatically update Built In templates -minecraft.settings.creator.auto_update_builtin_templates.comment=Updates happen when opening the creator once every IDE session, disable if updating fails consistently for you +minecraft.settings.creator.repos=Template Repositories: +minecraft.settings.creator.repos.column.name=Name +minecraft.settings.creator.repos.column.provider=Provider +minecraft.settings.creator.repo_config.title={0} Template Repo Configuration +minecraft.settings.creator.repo.default_name=My Repo +minecraft.settings.creator.repo.builtin_name=Built In + minecraft.settings.lang_template.display_name=Localization Template minecraft.settings.lang_template.scheme=Scheme: minecraft.settings.lang_template.project_must_be_selected=You must have selected a project for this! From 128255728f12305d11afb46021a2300ba9846299 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 16:35:55 +0200 Subject: [PATCH 081/118] Add back builtin provider --- src/main/kotlin/MinecraftConfigurable.kt | 2 +- src/main/kotlin/MinecraftSettings.kt | 6 +- .../providers/BuiltinTemplateProvider.kt | 85 ++++++++++++ .../custom/providers/LocalTemplateProvider.kt | 8 +- .../providers/RemoteTemplateProvider.kt | 128 +++++++++++------- .../custom/providers/TemplateProvider.kt | 3 - .../custom/providers/ZipTemplateProvider.kt | 20 ++- src/main/resources/META-INF/plugin.xml | 1 + 8 files changed, 181 insertions(+), 72 deletions(-) create mode 100644 src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index b08cd7357..3672374ea 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -182,7 +182,7 @@ class MinecraftConfigurable : Configurable { val configPanel = provider.setupConfigUi(selectedRepo.data, dataConsumer) ?: return@setEditAction - val dialog = object : DialogWrapper(null) { + val dialog = object : DialogWrapper(table, true) { init { init() } diff --git a/src/main/kotlin/MinecraftSettings.kt b/src/main/kotlin/MinecraftSettings.kt index 2c916212f..e819090b7 100644 --- a/src/main/kotlin/MinecraftSettings.kt +++ b/src/main/kotlin/MinecraftSettings.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.providers.RemoteTemplateProvider import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State @@ -58,8 +59,9 @@ class MinecraftSettings : PersistentStateComponent { companion object { - fun makeBuiltinRepo() = - TemplateRepo(MCDevBundle("minecraft.settings.creator.repo.builtin_name"), "remote", "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip\ntrue") + fun makeBuiltinRepo(): TemplateRepo { + return TemplateRepo(MCDevBundle("minecraft.settings.creator.repo.builtin_name"), "builtin", "true") + } } } diff --git a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt new file mode 100644 index 000000000..bf011392c --- /dev/null +++ b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt @@ -0,0 +1,85 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.providers + +import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.modalityState +import com.demonwav.mcdev.update.PluginUtil +import com.demonwav.mcdev.util.refreshSync +import com.demonwav.mcdev.util.virtualFile +import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.ui.dsl.builder.panel +import javax.swing.JComponent + +class BuiltinTemplateProvider : RemoteTemplateProvider() { + + private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip" + private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") + private var repoUpdated: Boolean = false + + override fun getLabel(): String = MCDevBundle("minecraft.settings.creator.repo.builtin_name") + + override val hasConfig: Boolean = true + + override fun init(indicator: ProgressIndicator, repos: List) { + if (repoUpdated || repos.none { it.data.toBoolean() }) { + // Auto update is disabled + return + } + + if (doUpdateRepo(indicator, getLabel(), builtinRepoUrl, builtinTemplatesPath)) { + repoUpdated = true + } + } + + override fun loadTemplates( + context: WizardContext, + repo: MinecraftSettings.TemplateRepo + ): Collection { + val repoRoot = builtinTemplatesPath.virtualFile + ?: return emptyList() + repoRoot.refreshSync(context.modalityState) + return TemplateProvider.findTemplates(context.modalityState, repoRoot) + } + + override fun setupConfigUi( + data: String, + dataSetter: (String) -> Unit + ): JComponent? { + val propertyGraph = PropertyGraph("BuiltinTemplateProvider config") + val autoUpdateProperty = propertyGraph.property(data.toBooleanStrictOrNull() != false) + + return panel { + row { + checkBox(MCDevBundle("creator.ui.custom.remote.auto_update.label")) + .bindSelected(autoUpdateProperty) + } + + onApply { + dataSetter(autoUpdateProperty.get().toString()) + } + } + } +} diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index 9990e00f4..a378cbcaf 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.modalityState +import com.demonwav.mcdev.util.refreshSync import com.demonwav.mcdev.util.virtualFile import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.application.ModalityState @@ -48,8 +49,11 @@ class LocalTemplateProvider : TemplateProvider { override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { val rootPath = Path.of(repo.data.trim()).absolute() - return rootPath.virtualFile?.let { TemplateProvider.findTemplates(context.modalityState, it) } - ?: emptyList() + val repoRoot = rootPath.virtualFile + ?: return emptyList() + val modalityState = context.modalityState + repoRoot.refreshSync(modalityState) + return TemplateProvider.findTemplates(modalityState, repoRoot) } override fun setupConfigUi( diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index d6c016d5e..23b14e8d3 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -26,6 +26,7 @@ import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil +import com.demonwav.mcdev.util.refreshSync import com.demonwav.mcdev.util.virtualFile import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull @@ -40,8 +41,11 @@ import com.intellij.openapi.observable.util.trim import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.util.io.FileUtil +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.bindText +import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation import com.intellij.util.io.ZipUtil @@ -52,7 +56,7 @@ import kotlin.io.path.listDirectoryEntries import kotlin.io.path.moveTo import kotlin.io.path.writeBytes -class RemoteTemplateProvider : TemplateProvider { +open class RemoteTemplateProvider : TemplateProvider { private var updatedTemplates = mutableSetOf() @@ -69,60 +73,76 @@ class RemoteTemplateProvider : TemplateProvider { continue } - indicator.text2 = "Updating remote repository ${repo.name}" - - val manager = FuelManager() - manager.proxy = selectProxy(remote.url) - val (_, _, result) = manager.get(remote.url) - .header("User-Agent", "github_org/minecraft-dev/${PluginUtil.pluginVersion}") - .header("Accepts", "application/json") - .timeout(10000) - .response() - - val data = result.onError { - thisLogger().warn("Could not fetch remote templates repository update at ${remote.url}", it) - }.getOrNull() ?: return - - try { - val remoteTemplatesDir = remote.getDestination(repo.name) - remoteTemplatesDir.createDirectories() - val zipPath = remoteTemplatesDir.resolveSibling("${repo.name}.zip") - zipPath.writeBytes(data) - FileUtil.deleteRecursively(remoteTemplatesDir) - ZipUtil.extract(zipPath, remoteTemplatesDir, null) - - // Loose way to find out if the url is a github repo archive - // In such cases there is a single directory in the root directory of the zip - // We simply move all its children to the base directory so the rest of the system uses the correct - // root directory for this repository - val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/(.*?).zip".toRegex() - val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(remote.url) - if (githubRepoArchiveMatcher != null) { - val repoName = githubRepoArchiveMatcher.groupValues[2] - val branchName = githubRepoArchiveMatcher.groupValues[3] - for (child in remoteTemplatesDir.resolve("$repoName-$branchName").listDirectoryEntries()) { - child.moveTo(remoteTemplatesDir.resolve(child.fileName)) - } - } - + if (doUpdateRepo(indicator, repo.name, remote.url, remote.getDestination(repo.name))) { updatedTemplates.add(remote.url) + } + } + } - thisLogger().info("Remote templates repository update applied successfully") - } catch (t: Throwable) { - if (t is ControlFlowException) { - throw t + protected fun doUpdateRepo( + indicator: ProgressIndicator, + repoName: String, + repoUrl: String, + destination: Path + ): Boolean { + indicator.text2 = "Updating remote repository $repoName" + + val manager = FuelManager() + manager.proxy = selectProxy(repoUrl) + val (_, _, result) = manager.get(repoUrl) + .header("User-Agent", "github_org/minecraft-dev/${PluginUtil.pluginVersion}") + .header("Accepts", "application/json") + .timeout(10000) + .response() + + val data = result.onError { + thisLogger().warn("Could not fetch remote templates repository update at ${repoUrl}", it) + }.getOrNull() ?: return false + + try { + val remoteTemplatesDir = destination + remoteTemplatesDir.createDirectories() + val zipPath = remoteTemplatesDir.resolveSibling("$repoName.zip") + zipPath.writeBytes(data) + FileUtil.deleteRecursively(remoteTemplatesDir) + ZipUtil.extract(zipPath, remoteTemplatesDir, null) + + // Loose way to find out if the url is a github repo archive + // In such cases there is a single directory in the root directory of the zip + // We simply move all its children to the base directory so the rest of the system uses the correct + // root directory for this repository + val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/(.*?).zip".toRegex() + val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(repoUrl) + if (githubRepoArchiveMatcher != null) { + val githubRepoName = githubRepoArchiveMatcher.groupValues[2] + val branchName = githubRepoArchiveMatcher.groupValues[3] + for (child in remoteTemplatesDir.resolve("$githubRepoName-$branchName").listDirectoryEntries()) { + child.moveTo(remoteTemplatesDir.resolve(child.fileName)) } - thisLogger().error("Failed to apply remote templates repository update", t, repo.toString()) } + + thisLogger().info("Remote templates repository update applied successfully") + return true + } catch (t: Throwable) { + if (t is ControlFlowException) { + throw t + } + thisLogger().error("Failed to apply remote templates repository update of $repoName", t) } + return false } - override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + override fun loadTemplates( + context: WizardContext, + repo: MinecraftSettings.TemplateRepo + ): Collection { val remote = RemoteTemplateRepo.deserialize(repo.data) ?: return emptyList() - return remote.getDestination(repo.name).virtualFile - ?.let { TemplateProvider.findTemplates(context.modalityState, it) } - ?: emptyList() + val repoRoot = remote.getDestination(repo.name).virtualFile + ?: return emptyList() + val modalityState = context.modalityState + repoRoot.refreshSync(modalityState) + return TemplateProvider.findTemplates(modalityState, repoRoot) } override fun setupConfigUi( @@ -137,6 +157,8 @@ class RemoteTemplateProvider : TemplateProvider { return panel { row(MCDevBundle("creator.ui.custom.remote.url.label")) { textField() + .align(AlignX.FILL) + .columns(COLUMNS_LARGE) .bindText(urlProperty) .textValidation(BuiltinValidations.nonBlank) } @@ -156,7 +178,7 @@ class RemoteTemplateProvider : TemplateProvider { override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element, modalityState) - private data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) { + data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) { fun getDestination(repoName: String): Path { return PathManager.getSystemDir().resolve("mcdev-templates").resolve(repoName) @@ -167,12 +189,14 @@ class RemoteTemplateProvider : TemplateProvider { companion object { fun deserialize(data: String): RemoteTemplateRepo? { val lines = data.lines() - if (lines.size < 2) { - return null + return when (lines.size) { + 0 -> null + 1 -> RemoteTemplateRepo(lines[0], true) + else -> { + val (url, autoUpdate) = lines + RemoteTemplateRepo(url, autoUpdate.toBoolean()) + } } - - val (url, autoUpdate) = lines - return RemoteTemplateRepo(url, autoUpdate.toBoolean()) } } } diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index bcf8702ca..919fb27a0 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -73,9 +73,6 @@ interface TemplateProvider { fun getAllKeys() = EP_NAME.extensionList.map { it.key } - fun getAll(): Collection = - EP_NAME.extensionList.mapNotNull { KeyedExtensionCollector.instantiate(it) } - fun findTemplates( modalityState: ModalityState, repoRoot: VirtualFile, diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 531c2de6e..497390fde 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.modalityState +import com.demonwav.mcdev.util.refreshSync import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory @@ -46,7 +47,13 @@ class ZipTemplateProvider : TemplateProvider { override val hasConfig: Boolean = true override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { - return loadTemplatesFrom(repo.data, context.modalityState) + val archiveRoot = repo.data + JarFileSystem.JAR_SEPARATOR + val fs = JarFileSystem.getInstance() + val rootFile = fs.refreshAndFindFileByPath(archiveRoot) + ?: return emptyList() + val modalityState = context.modalityState + rootFile.refreshSync(modalityState) + return TemplateProvider.findTemplates(modalityState, rootFile) } override fun setupConfigUi( @@ -83,15 +90,4 @@ class ZipTemplateProvider : TemplateProvider { override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = TemplateProvider.deserializeAndLoadVfs(element, modalityState) - - companion object { - - fun loadTemplatesFrom(archivePath: String, modalityState: ModalityState): List { - val archiveRoot = archivePath + JarFileSystem.JAR_SEPARATOR - val fs = JarFileSystem.getInstance() - val rootFile = fs.refreshAndFindFileByPath(archiveRoot) - ?: return emptyList() - return TemplateProvider.findTemplates(modalityState, rootFile) - } - } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 49c26a9ed..2751ca362 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -150,6 +150,7 @@ + From 4999c5cbf50aa5b53a57535cf833cff5fe1b042f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 16:42:07 +0200 Subject: [PATCH 082/118] Remove recent templates related code --- .../creator/custom/RecentProjectTemplates.kt | 67 ------------------- .../custom/providers/EmptyLoadedTemplate.kt | 3 - .../custom/providers/LoadedTemplate.kt | 2 - .../custom/providers/LocalTemplateProvider.kt | 4 -- .../providers/RemoteTemplateProvider.kt | 4 -- .../custom/providers/TemplateProvider.kt | 12 +--- .../custom/providers/VfsLoadedTemplate.kt | 41 ------------ .../custom/providers/ZipTemplateProvider.kt | 4 -- 8 files changed, 2 insertions(+), 135 deletions(-) delete mode 100644 src/main/kotlin/creator/custom/RecentProjectTemplates.kt diff --git a/src/main/kotlin/creator/custom/RecentProjectTemplates.kt b/src/main/kotlin/creator/custom/RecentProjectTemplates.kt deleted file mode 100644 index 6ff54deef..000000000 --- a/src/main/kotlin/creator/custom/RecentProjectTemplates.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2024 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.creator.custom - -import com.demonwav.mcdev.creator.custom.providers.LoadedTemplate -import com.intellij.openapi.components.BaseState -import com.intellij.openapi.components.SimplePersistentStateComponent -import com.intellij.openapi.components.State -import com.intellij.openapi.components.Storage -import com.intellij.openapi.components.service -import com.intellij.util.application -import com.intellij.util.xmlb.annotations.Attribute -import com.intellij.util.xmlb.annotations.Tag -import com.intellij.util.xmlb.annotations.Text -import com.intellij.util.xmlb.annotations.XCollection - -@State(name = "RecentProjectTemplates", storages = [Storage("minecraft_dev.xml")]) -class RecentProjectTemplates : SimplePersistentStateComponent(State()) { - - class State : BaseState() { - @get:XCollection(style = XCollection.Style.v2) - var templates by list() - } - - @Tag("template") - data class TemplateItem( - @get:Attribute("provider") - var provider: String, - @get:Text - var location: String - ) { - constructor() : this("", "") - } - - fun addNewTemplate(providerClassName: String, template: LoadedTemplate) { - val serialized = template.serialize() - ?: return - - val item = TemplateItem(providerClassName, serialized) - - state.templates.removeAll { it == item } - state.templates.add(0, item) - } - - companion object { - val instance: RecentProjectTemplates - get() = application.service() - } -} diff --git a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt index 71db87576..6f0cd2f45 100644 --- a/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/EmptyLoadedTemplate.kt @@ -37,7 +37,4 @@ object EmptyLoadedTemplate : LoadedTemplate { override fun loadTemplateContents(path: String): String? = throw UnsupportedOperationException("The empty template can't have contents") - - override fun serialize(): String? = - throw UnsupportedOperationException("The empty template can't have contents") } diff --git a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt index 14fa8157d..186d58f40 100644 --- a/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/LoadedTemplate.kt @@ -30,6 +30,4 @@ interface LoadedTemplate { val isValid: Boolean fun loadTemplateContents(path: String): String? - - fun serialize(): String? } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index a378cbcaf..c891fd667 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -26,7 +26,6 @@ import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.util.refreshSync import com.demonwav.mcdev.util.virtualFile import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.ui.validation.validationErrorIf @@ -89,7 +88,4 @@ class LocalTemplateProvider : TemplateProvider { } } } - - override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = - TemplateProvider.deserializeAndLoadVfs(element, modalityState) } diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index 23b14e8d3..d8e943ed2 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -32,7 +32,6 @@ import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.onError import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.PathManager import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger @@ -175,9 +174,6 @@ open class RemoteTemplateProvider : TemplateProvider { } } - override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = - TemplateProvider.deserializeAndLoadVfs(element, modalityState) - data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) { fun getDestination(repoName: String): Path { diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 919fb27a0..7a095f0a4 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -61,8 +61,6 @@ interface TemplateProvider { fun setupConfigUi(data: String, dataSetter: (String) -> Unit): JComponent? - fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? - companion object { private val EP_NAME = @@ -86,7 +84,7 @@ interface TemplateProvider { findTemplates(modalityState, repoRoot, child, templates, bundle) } else if (child.name.endsWith(".mcdev.template.json")) { try { - createVfsLoadedTemplate(modalityState, repoRoot, directory, child, bundle = bundle)?.let( + createVfsLoadedTemplate(modalityState, directory, child, bundle = bundle)?.let( templates::add ) } catch (t: Throwable) { @@ -159,7 +157,6 @@ interface TemplateProvider { fun createVfsLoadedTemplate( modalityState: ModalityState, - repoRoot: VirtualFile, templateRoot: VirtualFile, descriptorFile: VirtualFile, tooltip: String? = null, @@ -205,12 +202,7 @@ interface TemplateProvider { } } - return VfsLoadedTemplate(repoRoot, templateRoot, descriptorFile, label, tooltip, descriptor, true) - } - - fun deserializeAndLoadVfs(element: String, modalityState: ModalityState): LoadedTemplate? { - val serialized = Gson().fromJson(element) - return serialized.load(modalityState) + return VfsLoadedTemplate(templateRoot, label, tooltip, descriptor, true) } } } diff --git a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt index ec2f18495..aa34b456c 100644 --- a/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt +++ b/src/main/kotlin/creator/custom/providers/VfsLoadedTemplate.kt @@ -21,18 +21,12 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.creator.custom.TemplateDescriptor -import com.google.gson.Gson -import com.intellij.openapi.application.ModalityState -import com.intellij.openapi.vfs.JarFileSystem -import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.readText import java.io.FileNotFoundException class VfsLoadedTemplate( - val repoRoot: VirtualFile, val templateRoot: VirtualFile, - val descriptorFile: VirtualFile, override val label: String, override val tooltip: String? = null, override val descriptor: TemplateDescriptor, @@ -46,39 +40,4 @@ class VfsLoadedTemplate( virtualFile.refresh(false, false) return virtualFile.readText() } - - data class Serialized( - val repoRoot: String, - val templateRoot: String, - val descriptorPath: String - ) { - fun load(modalityState: ModalityState): LoadedTemplate? { - val fs = if (repoRoot.contains(JarFileSystem.JAR_SEPARATOR)) { - JarFileSystem.getInstance() - } else { - LocalFileSystem.getInstance() - } - val repoRoot = fs.refreshAndFindFileByPath(repoRoot) - ?: return null - val templateRoot = fs.refreshAndFindFileByPath(templateRoot) - ?: return null - val descriptorFile = fs.refreshAndFindFileByPath(descriptorPath) - ?: return null - val tooltip = descriptorFile.path - - val bundle = TemplateProvider.loadMessagesBundle(modalityState, repoRoot) - return TemplateProvider.createVfsLoadedTemplate( - modalityState, - repoRoot, - templateRoot, - descriptorFile, - tooltip, - bundle - ) - } - } - - override fun serialize(): String? { - return Gson().toJson(Serialized(repoRoot.path, templateRoot.path, descriptorFile.path)) - } } diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 497390fde..2b58f622e 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -25,7 +25,6 @@ import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.util.refreshSync import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.ui.validation.validationErrorIf @@ -87,7 +86,4 @@ class ZipTemplateProvider : TemplateProvider { } } } - - override fun deserializeAndLoad(element: String, modalityState: ModalityState): LoadedTemplate? = - TemplateProvider.deserializeAndLoadVfs(element, modalityState) } From 11d97019c4dffda9d1c584838609ce63d8528d6c Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 17:06:32 +0200 Subject: [PATCH 083/118] Convert recursive virtualfile loop into visitor --- .../custom/providers/TemplateProvider.kt | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 7a095f0a4..1a1b08b78 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -35,9 +35,11 @@ import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.RequiredElement import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.util.KeyedExtensionCollector +import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileVisitor +import com.intellij.openapi.vfs.isFile import com.intellij.openapi.vfs.readText import com.intellij.serviceContainer.BaseKeyedLazyInstance import com.intellij.util.KeyedLazyInstance @@ -74,34 +76,35 @@ interface TemplateProvider { fun findTemplates( modalityState: ModalityState, repoRoot: VirtualFile, - directory: VirtualFile = repoRoot, templates: MutableList = mutableListOf(), bundle: ResourceBundle? = loadMessagesBundle(modalityState, repoRoot) ): List { - for (child in directory.children) { // TODO use visitor instead of loop - ProgressManager.checkCanceled() - if (child.isDirectory) { - findTemplates(modalityState, repoRoot, child, templates, bundle) - } else if (child.name.endsWith(".mcdev.template.json")) { + val visitor = object : VirtualFileVisitor() { + override fun visitFile(file: VirtualFile): Boolean { + if (!file.isFile || !file.name.endsWith(".mcdev.template.json")) { + return true + } + try { - createVfsLoadedTemplate(modalityState, directory, child, bundle = bundle)?.let( - templates::add - ) + createVfsLoadedTemplate(modalityState, file.parent, file, bundle = bundle) + ?.let(templates::add) } catch (t: Throwable) { if (t is ControlFlowException) { throw t } - val attachment = runCatching { Attachment(child.name, child.readText()) }.getOrNull() + val attachment = runCatching { Attachment(file.name, file.readText()) }.getOrNull() if (attachment != null) { - thisLogger().error("Failed to load template ${child.path}", t, attachment) + thisLogger().error("Failed to load template ${file.path}", t, attachment) } else { - thisLogger().error("Failed to load template ${child.path}", t) + thisLogger().error("Failed to load template ${file.path}", t) } } + + return true } } - + VfsUtilCore.visitChildrenRecursively(repoRoot, visitor) return templates } From bb93e27af7f158a691662486dbc75933bee218f7 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 17:35:42 +0200 Subject: [PATCH 084/118] Make provider label a property and localize it --- src/main/kotlin/MinecraftConfigurable.kt | 3 --- src/main/kotlin/MinecraftSettings.kt | 1 - src/main/kotlin/creator/custom/CustomPlatformStep.kt | 2 +- .../creator/custom/providers/BuiltinTemplateProvider.kt | 4 ++-- .../creator/custom/providers/LocalTemplateProvider.kt | 7 +++++-- .../creator/custom/providers/RemoteTemplateProvider.kt | 4 ++-- .../kotlin/creator/custom/providers/TemplateProvider.kt | 2 +- .../kotlin/creator/custom/providers/ZipTemplateProvider.kt | 7 +++++-- .../resources/messages/MinecraftDevelopment.properties | 5 +++++ 9 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index 3672374ea..5e97a4b3d 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -29,8 +29,6 @@ import com.intellij.openapi.options.Configurable import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.DialogWrapper -import com.intellij.openapi.util.NlsContexts -import com.intellij.psi.impl.cache.impl.id.IdDataConsumer import com.intellij.ui.ComboBoxTableCellRenderer import com.intellij.ui.EnumComboBoxModel import com.intellij.ui.ToolbarDecorator @@ -39,7 +37,6 @@ import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.BottomGap import com.intellij.ui.dsl.builder.MutableProperty -import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel diff --git a/src/main/kotlin/MinecraftSettings.kt b/src/main/kotlin/MinecraftSettings.kt index e819090b7..cc7cc00da 100644 --- a/src/main/kotlin/MinecraftSettings.kt +++ b/src/main/kotlin/MinecraftSettings.kt @@ -21,7 +21,6 @@ package com.demonwav.mcdev import com.demonwav.mcdev.asset.MCDevBundle -import com.demonwav.mcdev.creator.custom.providers.RemoteTemplateProvider import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.State diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index e00f697b1..d711a1064 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -236,7 +236,7 @@ class CustomPlatformStep( ProgressManager.checkCanceled() val provider = TemplateProvider.get(providerKey) ?: continue - indicator.text = provider.getLabel() + indicator.text = provider.label runCatching { provider.init(indicator, repos) } .getOrLogException(logger()) } diff --git a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt index bf011392c..7c13543e4 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt @@ -39,7 +39,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() { private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") private var repoUpdated: Boolean = false - override fun getLabel(): String = MCDevBundle("minecraft.settings.creator.repo.builtin_name") + override val label: String = MCDevBundle("template.provider.builtin.label") override val hasConfig: Boolean = true @@ -49,7 +49,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() { return } - if (doUpdateRepo(indicator, getLabel(), builtinRepoUrl, builtinTemplatesPath)) { + if (doUpdateRepo(indicator, label, builtinRepoUrl, builtinTemplatesPath)) { repoUpdated = true } } diff --git a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt index c891fd667..d08fb037c 100644 --- a/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/LocalTemplateProvider.kt @@ -42,11 +42,14 @@ import kotlin.io.path.absolute class LocalTemplateProvider : TemplateProvider { - override fun getLabel(): String = "Local" + override val label: String = MCDevBundle("template.provider.local.label") override val hasConfig: Boolean = true - override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + override fun loadTemplates( + context: WizardContext, + repo: MinecraftSettings.TemplateRepo + ): Collection { val rootPath = Path.of(repo.data.trim()).absolute() val repoRoot = rootPath.virtualFile ?: return emptyList() diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index d8e943ed2..53246d177 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -59,7 +59,7 @@ open class RemoteTemplateProvider : TemplateProvider { private var updatedTemplates = mutableSetOf() - override fun getLabel(): String = "Remote" + override val label: String = MCDevBundle("template.provider.remote.label") override val hasConfig: Boolean = true @@ -95,7 +95,7 @@ open class RemoteTemplateProvider : TemplateProvider { .response() val data = result.onError { - thisLogger().warn("Could not fetch remote templates repository update at ${repoUrl}", it) + thisLogger().warn("Could not fetch remote templates repository update at $repoUrl", it) }.getOrNull() ?: return false try { diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 1a1b08b78..6d37b567b 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -53,7 +53,7 @@ import javax.swing.JComponent */ interface TemplateProvider { - fun getLabel(): String + val label: String val hasConfig: Boolean diff --git a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt index 2b58f622e..6cae4d3ce 100644 --- a/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/ZipTemplateProvider.kt @@ -41,11 +41,14 @@ import kotlin.io.path.isRegularFile class ZipTemplateProvider : TemplateProvider { - override fun getLabel(): String = "Archive" + override val label: String = MCDevBundle("template.provider.zip.label") override val hasConfig: Boolean = true - override fun loadTemplates(context: WizardContext, repo: MinecraftSettings.TemplateRepo): Collection { + override fun loadTemplates( + context: WizardContext, + repo: MinecraftSettings.TemplateRepo + ): Collection { val archiveRoot = repo.data + JarFileSystem.JAR_SEPARATOR val fs = JarFileSystem.getInstance() val rootFile = fs.refreshAndFindFileByPath(archiveRoot) diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index f6cd1fbdc..1211f3678 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -281,3 +281,8 @@ minecraft.settings.lang_template.comment=You may edit the template used fo minecraft.settings.translation=Translation minecraft.settings.translation.force_json_translation_file=Force JSON translation file (1.13+) minecraft.settings.translation.use_custom_convert_template=Use custom template for convert literal to translation + +template.provider.builtin.label=Built In +template.provider.remote.label=Remote +template.provider.local.label=Local +template.provider.zip.label=Archive From 226d98651e5945182eda85ff1b62fbc719b53580 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 18:03:30 +0200 Subject: [PATCH 085/118] Move repo table code outside of MinecraftConfigurable --- src/main/kotlin/MinecraftConfigurable.kt | 97 +-------------- .../creator/custom/TemplateRepoTable.kt | 113 ++++++++++++++++++ .../custom/providers/TemplateProvider.kt | 2 +- 3 files changed, 120 insertions(+), 92 deletions(-) create mode 100644 src/main/kotlin/creator/custom/TemplateRepoTable.kt diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index 5e97a4b3d..98d829058 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.asset.PlatformAssets import com.demonwav.mcdev.creator.custom.providers.TemplateProvider +import com.demonwav.mcdev.creator.custom.templateRepoTable import com.demonwav.mcdev.update.ConfigurePluginUpdatesDialog import com.intellij.ide.projectView.ProjectView import com.intellij.openapi.options.Configurable @@ -104,102 +105,16 @@ class MinecraftConfigurable : Configurable { } } - val nameColumn = object : - ColumnInfo( - MCDevBundle("minecraft.settings.creator.repos.column.name") - ) { - - override fun valueOf(item: MinecraftSettings.TemplateRepo?): String? { - return item?.name - } - - override fun setValue(item: MinecraftSettings.TemplateRepo?, value: String?) { - item?.name = value ?: MCDevBundle("minecraft.settings.creator.repo.default_name") - } - - override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true - } - - val providerColumn = object : ColumnInfo( - MCDevBundle("minecraft.settings.creator.repos.column.provider") - ) { - - override fun valueOf(item: MinecraftSettings.TemplateRepo?): ListWithSelection? { - val providers = TemplateProvider.getAllKeys() - val list = ListWithSelection(providers) - list.select(item?.provider?.takeUnless { it.isBlank() }) - - return list - } - - override fun setValue(item: MinecraftSettings.TemplateRepo?, value: Any?) { - item?.provider = value as? String ?: "local" - } - - override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true - - override fun getRenderer(item: MinecraftSettings.TemplateRepo?): TableCellRenderer? { - return ComboBoxTableCellRenderer.INSTANCE - } - - override fun getEditor(item: MinecraftSettings.TemplateRepo?): TableCellEditor? { - return ComboBoxTableCellEditor.INSTANCE - } - } - - val model = object : ListTableModel(nameColumn, providerColumn) { - override fun addRow() { - val defaultName = MCDevBundle("minecraft.settings.creator.repo.default_name") - addRow(MinecraftSettings.TemplateRepo(defaultName, "local", "")) - } - } group(MCDevBundle("minecraft.settings.creator")) { row(MCDevBundle("minecraft.settings.creator.repos")) {} row { - val table = TableView() - table.setShowGrid(true) - table.model = model - table.tableHeader.reorderingAllowed = false - - val decoratedTable = ToolbarDecorator.createDecorator(table) - .setEditActionUpdater { - val selectedRepo = table.selection.firstOrNull() - ?: return@setEditActionUpdater false - val provider = TemplateProvider.get(selectedRepo.provider) - ?: return@setEditActionUpdater false - return@setEditActionUpdater provider.hasConfig - } - .setEditAction { - val selectedRepo = table.selection.firstOrNull() - ?: return@setEditAction - val provider = TemplateProvider.get(selectedRepo.provider) - ?: return@setEditAction - val dataConsumer = { data: String -> selectedRepo.data = data } - val configPanel = provider.setupConfigUi(selectedRepo.data, dataConsumer) - ?: return@setEditAction - - val dialog = object : DialogWrapper(table, true) { - init { - init() - } - - override fun createCenterPanel(): JComponent = configPanel - } - dialog.title = MCDevBundle("minecraft.settings.creator.repo_config.title", selectedRepo.name) - dialog.show() - } - .createPanel() - cell(decoratedTable) - .align(Align.FILL) - .bind( - { _ -> model.items }, - { _, repos -> model.items = repos; }, - MutableProperty( - { settings.creatorTemplateRepos.toMutableList() }, - { settings.creatorTemplateRepos = it } - ) + templateRepoTable( + MutableProperty( + { settings.creatorTemplateRepos.toMutableList() }, + { settings.creatorTemplateRepos = it } ) + ) }.resizableRow() } diff --git a/src/main/kotlin/creator/custom/TemplateRepoTable.kt b/src/main/kotlin/creator/custom/TemplateRepoTable.kt new file mode 100644 index 000000000..01e613cf6 --- /dev/null +++ b/src/main/kotlin/creator/custom/TemplateRepoTable.kt @@ -0,0 +1,113 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.MinecraftSettings +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.providers.TemplateProvider +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.ui.ComboBoxTableCellRenderer +import com.intellij.ui.ToolbarDecorator +import com.intellij.ui.dsl.builder.Cell +import com.intellij.ui.dsl.builder.MutableProperty +import com.intellij.ui.dsl.builder.Row +import com.intellij.ui.table.TableView +import com.intellij.util.ListWithSelection +import com.intellij.util.ui.ColumnInfo +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.ListTableModel +import com.intellij.util.ui.table.ComboBoxTableCellEditor +import java.awt.Dimension +import javax.swing.JComponent +import javax.swing.JPanel +import javax.swing.table.TableCellEditor +import javax.swing.table.TableCellRenderer + +private object NameColumn : ColumnInfo( + MCDevBundle("minecraft.settings.creator.repos.column.name") +) { + override fun valueOf(item: MinecraftSettings.TemplateRepo?): String? { + return item?.name + } + + override fun setValue(item: MinecraftSettings.TemplateRepo?, value: String?) { + item?.name = value ?: MCDevBundle("minecraft.settings.creator.repo.default_name") + } + + override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true +} + +private object ProviderColumn : ColumnInfo( + MCDevBundle("minecraft.settings.creator.repos.column.provider") +) { + override fun valueOf(item: MinecraftSettings.TemplateRepo?): ListWithSelection? { + val providers = TemplateProvider.getAllKeys() + val list = ListWithSelection(providers) + list.select(item?.provider?.takeIf(providers::contains)) + + return list + } + + override fun setValue(item: MinecraftSettings.TemplateRepo?, value: Any?) { + item?.provider = value as? String ?: "local" + } + + override fun isCellEditable(item: MinecraftSettings.TemplateRepo?): Boolean = true + + override fun getRenderer(item: MinecraftSettings.TemplateRepo?): TableCellRenderer? { + return ComboBoxTableCellRenderer.INSTANCE + } + + override fun getEditor(item: MinecraftSettings.TemplateRepo?): TableCellEditor? { + return ComboBoxTableCellEditor.INSTANCE + } +} + +fun Row.templateRepoTable( + prop: MutableProperty> +): Cell { + val model = object : ListTableModel(NameColumn, ProviderColumn) { + override fun addRow() { + val defaultName = MCDevBundle("minecraft.settings.creator.repo.default_name") + addRow(MinecraftSettings.TemplateRepo(defaultName, "local", "")) + } + } + + val table = TableView(model) + table.setShowGrid(true) + table.tableHeader.reorderingAllowed = false + + val decoratedTable = ToolbarDecorator.createDecorator(table) + .setPreferredSize(Dimension(JBUI.scale(300), JBUI.scale(200))) + .setEditActionUpdater { + val selectedRepo = table.selection.firstOrNull() + ?: return@setEditActionUpdater false + val provider = TemplateProvider.get(selectedRepo.provider) + ?: return@setEditActionUpdater false + return@setEditActionUpdater provider.hasConfig + } + .setEditAction { + val selectedRepo = table.selection.firstOrNull() + ?: return@setEditAction + val provider = TemplateProvider.get(selectedRepo.provider) + ?: return@setEditAction + val dataConsumer = { data: String -> selectedRepo.data = data } + val configPanel = provider.setupConfigUi(selectedRepo.data, dataConsumer) + ?: return@setEditAction + + val dialog = object : DialogWrapper(table, true) { + init { + init() + } + + override fun createCenterPanel(): JComponent = configPanel + } + dialog.title = MCDevBundle("minecraft.settings.creator.repo_config.title", selectedRepo.name) + dialog.show() + } + .createPanel() + return cell(decoratedTable) + .bind( + { _ -> model.items }, + { _, repos -> model.items = repos; }, + prop + ) +} diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 6d37b567b..79b0a207b 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -71,7 +71,7 @@ interface TemplateProvider { fun get(key: String): TemplateProvider? = COLLECTOR.findSingle(key) - fun getAllKeys() = EP_NAME.extensionList.map { it.key } + fun getAllKeys() = EP_NAME.extensionList.mapNotNull { it.key } fun findTemplates( modalityState: ModalityState, From d859d81520f1bfe19a7e224b89b3907f58db658c Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 15 Jun 2024 18:39:21 +0200 Subject: [PATCH 086/118] Fix differences in creator properties naming and ctor parameters order --- .../creator/custom/CustomPlatformStep.kt | 6 +++++- .../creator/custom/TemplateRepoTable.kt | 20 +++++++++++++++++++ ...=> ArchitecturyVersionsCreatorProperty.kt} | 20 +++++++++---------- .../custom/types/BooleanCreatorProperty.kt | 8 ++++---- .../BuildSystemCoordinatesCreatorProperty.kt | 6 +++--- .../custom/types/ClassFqnCreatorProperty.kt | 8 ++++---- .../custom/types/CreatorPropertyFactory.kt | 4 ++-- .../custom/types/ExternalCreatorProperty.kt | 2 +- ...ty.kt => FabricVersionsCreatorProperty.kt} | 14 ++++++------- ...rty.kt => ForgeVersionsCreatorProperty.kt} | 6 +++--- .../types/InlineStringListCreatorProperty.kt | 8 ++++---- .../custom/types/IntegerCreatorProperty.kt | 8 ++++---- .../custom/types/JdkCreatorProperty.kt | 8 ++++---- ...eProperty.kt => LicenseCreatorProperty.kt} | 8 ++++---- ...=> MavenArtifactVersionCreatorProperty.kt} | 10 +++++----- ....kt => NeoForgeVersionsCreatorProperty.kt} | 6 +++--- ...roperty.kt => ParchmentCreatorProperty.kt} | 6 +++--- .../types/SemanticVersionCreatorProperty.kt | 8 ++++---- .../custom/types/SimpleCreatorProperty.kt | 2 +- .../custom/types/StringCreatorProperty.kt | 8 ++++---- src/main/resources/META-INF/plugin.xml | 14 ++++++------- 21 files changed, 102 insertions(+), 78 deletions(-) rename src/main/kotlin/creator/custom/types/{ArchitecturyVersionsProperty.kt => ArchitecturyVersionsCreatorProperty.kt} (96%) rename src/main/kotlin/creator/custom/types/{FabricVersionsProperty.kt => FabricVersionsCreatorProperty.kt} (97%) rename src/main/kotlin/creator/custom/types/{ForgeVersionsProperty.kt => ForgeVersionsCreatorProperty.kt} (98%) rename src/main/kotlin/creator/custom/types/{LicenseProperty.kt => LicenseCreatorProperty.kt} (96%) rename src/main/kotlin/creator/custom/types/{MavenArtifactVersionProperty.kt => MavenArtifactVersionCreatorProperty.kt} (94%) rename src/main/kotlin/creator/custom/types/{NeoForgeVersionsProperty.kt => NeoForgeVersionsCreatorProperty.kt} (98%) rename src/main/kotlin/creator/custom/types/{ParchmentProperty.kt => ParchmentCreatorProperty.kt} (98%) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index d711a1064..059ebd153 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -316,7 +316,11 @@ class CustomPlatformStep( val baseData = data.getUserData(NewProjectWizardBaseData.KEY) ?: return thisLogger().error("Could not find wizard base data") - properties["PROJECT_NAME"] = ExternalCreatorProperty(propertyGraph, properties, baseData.nameProperty) + properties["PROJECT_NAME"] = ExternalCreatorProperty( + graph = propertyGraph, + properties = properties, + graphProperty = baseData.nameProperty + ) placeholder.component = panel { val reporter = TemplateValidationReporterImpl() diff --git a/src/main/kotlin/creator/custom/TemplateRepoTable.kt b/src/main/kotlin/creator/custom/TemplateRepoTable.kt index 01e613cf6..d14f023f0 100644 --- a/src/main/kotlin/creator/custom/TemplateRepoTable.kt +++ b/src/main/kotlin/creator/custom/TemplateRepoTable.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.MinecraftSettings diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt similarity index 96% rename from src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt rename to src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt index 0e2aafefc..bb062b929 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt @@ -50,9 +50,9 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class ArchitecturyVersionsProperty( - graph: PropertyGraph, +class ArchitecturyVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ) : CreatorProperty(descriptor, graph, properties) { @@ -287,14 +287,14 @@ class ArchitecturyVersionsProperty( val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } val architecturyVersionsJob = asyncIO { ArchitecturyVersion.downloadData() } - this@ArchitecturyVersionsProperty.forgeVersions = forgeVersionsJob.await() - this@ArchitecturyVersionsProperty.neoForgeVersions = neoForgeVersionsJob.await() - this@ArchitecturyVersionsProperty.fabricVersions = fabricVersionsJob.await() - this@ArchitecturyVersionsProperty.loomVersions = loomVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.forgeVersions = forgeVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.neoForgeVersions = neoForgeVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.fabricVersions = fabricVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.loomVersions = loomVersionsJob.await() .mapNotNull(SemanticVersion::tryParse) .sortedDescending() - this@ArchitecturyVersionsProperty.fabricApiVersions = fabricApiVersionsJob.await() - this@ArchitecturyVersionsProperty.architecturyVersions = architecturyVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.fabricApiVersions = fabricApiVersionsJob.await() + this@ArchitecturyVersionsCreatorProperty.architecturyVersions = architecturyVersionsJob.await() withContext(Dispatchers.Swing) { val fabricVersions = fabricVersions @@ -442,9 +442,9 @@ class ArchitecturyVersionsProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = ArchitecturyVersionsProperty(graph, descriptor, properties) + ): CreatorProperty<*> = ArchitecturyVersionsCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index a32819c11..a649f3773 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -30,10 +30,10 @@ import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindSelected class BooleanCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { override fun createDefaultValue(raw: Any?): Boolean = raw as? Boolean ?: false @@ -59,9 +59,9 @@ class BooleanCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = BooleanCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = BooleanCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index 0bf9467b6..4659c28fc 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -45,8 +45,8 @@ private val nonExampleValidation = validationErrorIf(MCDevBundle("creato } class BuildSystemCoordinatesCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ) : CreatorProperty(descriptor, graph, properties) { @@ -127,9 +127,9 @@ class BuildSystemCoordinatesCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = BuildSystemCoordinatesCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = BuildSystemCoordinatesCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 049ade355..0e782a502 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -36,10 +36,10 @@ import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.textValidation class ClassFqnCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { override fun createDefaultValue(raw: Any?): ClassFqn = ClassFqn(raw as? String ?: "") @@ -74,9 +74,9 @@ class ClassFqnCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = ClassFqnCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = ClassFqnCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt index b75568ac9..8d3689d50 100644 --- a/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt +++ b/src/main/kotlin/creator/custom/types/CreatorPropertyFactory.kt @@ -45,13 +45,13 @@ interface CreatorPropertyFactory { graph: PropertyGraph, properties: Map> ): CreatorProperty<*>? { - return COLLECTOR.findSingle(type)?.create(graph, descriptor, properties) + return COLLECTOR.findSingle(type)?.create(descriptor, graph, properties) } } fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ): CreatorProperty<*> } diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index 7787b93bc..ec38b3993 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -28,10 +28,10 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.Panel class ExternalCreatorProperty( + descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", default = ""), graph: PropertyGraph, properties: Map>, override val graphProperty: GraphProperty, - descriptor: TemplatePropertyDescriptor = TemplatePropertyDescriptor("", "", "", default = ""), ) : CreatorProperty(descriptor, graph, properties) { override fun setupProperty(reporter: TemplateValidationReporter) = Unit diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt similarity index 97% rename from src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt rename to src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt index 74364cadf..39908b521 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt @@ -49,9 +49,9 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class FabricVersionsProperty( - graph: PropertyGraph, +class FabricVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ) : CreatorProperty(descriptor, graph, properties) { @@ -221,11 +221,11 @@ class FabricVersionsProperty( } val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } - this@FabricVersionsProperty.fabricVersions = fabricVersionsJob.await() - this@FabricVersionsProperty.loomVersions = loomVersionsJob.await() + this@FabricVersionsCreatorProperty.fabricVersions = fabricVersionsJob.await() + this@FabricVersionsCreatorProperty.loomVersions = loomVersionsJob.await() .mapNotNull(SemanticVersion::tryParse) .sortedDescending() - this@FabricVersionsProperty.fabricApiVersions = fabricApiVersionsJob.await() + this@FabricVersionsCreatorProperty.fabricApiVersions = fabricApiVersionsJob.await() withContext(Dispatchers.Swing) { val fabricVersions = fabricVersions @@ -310,9 +310,9 @@ class FabricVersionsProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = FabricVersionsProperty(graph, descriptor, properties) + ): CreatorProperty<*> = FabricVersionsCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt similarity index 98% rename from src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt rename to src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt index 056b44431..ffb7e264b 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class ForgeVersionsProperty( +class ForgeVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> @@ -157,9 +157,9 @@ class ForgeVersionsProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = ForgeVersionsProperty(descriptor, graph, properties) + ): CreatorProperty<*> = ForgeVersionsCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt index 3cac2509e..b713d8639 100644 --- a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -30,10 +30,10 @@ import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns class InlineStringListCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { override fun createDefaultValue(raw: Any?): StringList = deserialize(raw as? String ?: "") @@ -54,9 +54,9 @@ class InlineStringListCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = InlineStringListCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = InlineStringListCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 4b4162086..4e7e650e5 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -34,10 +34,10 @@ import com.intellij.ui.dsl.builder.bindIntText import com.intellij.ui.dsl.builder.columns class IntegerCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 @@ -84,9 +84,9 @@ class IntegerCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = IntegerCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = IntegerCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index a2ef6377b..b09efeb6c 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -32,10 +32,10 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.ui.dsl.builder.Panel class JdkCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { private lateinit var jdkComboBox: JdkComboBoxWithPreference @@ -69,9 +69,9 @@ class JdkCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = JdkCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = JdkCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/LicenseProperty.kt b/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt similarity index 96% rename from src/main/kotlin/creator/custom/types/LicenseProperty.kt rename to src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt index ed4a2e4aa..2e0a61d68 100644 --- a/src/main/kotlin/creator/custom/types/LicenseProperty.kt +++ b/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt @@ -33,9 +33,9 @@ import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem import java.time.ZonedDateTime -class LicenseProperty( - graph: PropertyGraph, +class LicenseCreatorProperty( descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ) : CreatorProperty(descriptor, graph, properties) { @@ -66,9 +66,9 @@ class LicenseProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = LicenseProperty(graph, descriptor, properties) + ): CreatorProperty<*> = LicenseCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt similarity index 94% rename from src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt rename to src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index c0955ed84..9c5656afb 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -36,11 +36,11 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class MavenArtifactVersionProperty( - graph: PropertyGraph, +class MavenArtifactVersionCreatorProperty( descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SemanticVersionCreatorProperty(graph, descriptor, properties) { +) : SemanticVersionCreatorProperty(descriptor, graph, properties) { val sourceUrl: String get() = descriptor.parameters!!["sourceUrl"] as String @@ -88,9 +88,9 @@ class MavenArtifactVersionProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = MavenArtifactVersionProperty(graph, descriptor, properties) + ): CreatorProperty<*> = MavenArtifactVersionCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt similarity index 98% rename from src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt rename to src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index bedd8521b..539f10234 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class NeoForgeVersionsProperty( +class NeoForgeVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> @@ -173,9 +173,9 @@ class NeoForgeVersionsProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = NeoForgeVersionsProperty(descriptor, graph, properties) + ): CreatorProperty<*> = NeoForgeVersionsCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt similarity index 98% rename from src/main/kotlin/creator/custom/types/ParchmentProperty.kt rename to src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt index d88b55e21..d10529205 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext -class ParchmentProperty( +class ParchmentCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> @@ -243,9 +243,9 @@ class ParchmentProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = ParchmentProperty(descriptor, graph, properties) + ): CreatorProperty<*> = ParchmentCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 329bf06a1..991da2543 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -31,10 +31,10 @@ import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns open class SemanticVersionCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { override fun createDefaultValue(raw: Any?): SemanticVersion = SemanticVersion.tryParse(raw as? String ?: "") ?: SemanticVersion(emptyList()) @@ -81,9 +81,9 @@ open class SemanticVersionCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = SemanticVersionCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = SemanticVersionCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 65d75c1e5..2716033a9 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -32,8 +32,8 @@ import javax.swing.DefaultListCellRenderer import javax.swing.JList abstract class SimpleCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> ) : CreatorProperty(descriptor, graph, properties) { diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 8338baae3..9256ac95a 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -34,10 +34,10 @@ import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.textValidation class StringCreatorProperty( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(graph, descriptor, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties) { private var validationRegex: Regex? = null @@ -97,9 +97,9 @@ class StringCreatorProperty( class Factory : CreatorPropertyFactory { override fun create( - graph: PropertyGraph, descriptor: TemplatePropertyDescriptor, + graph: PropertyGraph, properties: Map> - ): CreatorProperty<*> = StringCreatorProperty(graph, descriptor, properties) + ): CreatorProperty<*> = StringCreatorProperty(descriptor, graph, properties) } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2751ca362..8869a6aec 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -134,19 +134,19 @@ - + - - + + - - - - + + + + From fb2b6cb394ffa19b1a42bef468f69ce9e8fe741c Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 02:10:11 +0200 Subject: [PATCH 087/118] Some work towards more extensible derivations --- .../creator/custom/PropertyDerivation.kt | 16 +++++ .../custom/ReplacePropertyDerivation.kt | 72 +++++++++++++++++++ .../custom/SelectPropertyDerivation.kt | 43 +++++++++++ .../creator/custom/types/CreatorProperty.kt | 15 ++++ .../custom/types/StringCreatorProperty.kt | 42 ++++++----- 5 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/creator/custom/PropertyDerivation.kt create mode 100644 src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt create mode 100644 src/main/kotlin/creator/custom/SelectPropertyDerivation.kt diff --git a/src/main/kotlin/creator/custom/PropertyDerivation.kt b/src/main/kotlin/creator/custom/PropertyDerivation.kt new file mode 100644 index 000000000..a87864715 --- /dev/null +++ b/src/main/kotlin/creator/custom/PropertyDerivation.kt @@ -0,0 +1,16 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.creator.custom.types.CreatorProperty + +fun interface PreparedDerivation { + fun derive(parentValues: List): Any? +} + +interface PropertyDerivationFactory { + + fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? +} diff --git a/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt b/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt new file mode 100644 index 000000000..9f4c569ee --- /dev/null +++ b/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt @@ -0,0 +1,72 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.creator.custom.types.CreatorProperty + +class ReplacePropertyDerivation( + val regex: Regex, + val replacement: String, + val maxLength: Int?, +) : PreparedDerivation { + + override fun derive(parentValues: List): Any? { + val projectName = parentValues.first() as? String + ?: return null + + val sanitized = projectName.lowercase().replace(regex, replacement) + if (maxLength != null && sanitized.length > maxLength) { + return sanitized.substring(0, maxLength) + } + + return sanitized + } + + companion object : PropertyDerivationFactory { + + override fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? { + if (derivation.parameters == null) { + reporter.error("Missing parameters") + return null + } + + if (parents.isNullOrEmpty()) { + reporter.error("Missing parent value") + return null + } + + if (parents.size > 2) { + reporter.warn("More than one parent defined") + } + + if (parents.first()?.get() !is String) { + reporter.error("Parent property must produce a string value") + return null + } + + val regexString = derivation.parameters["regex"] as? String + if (regexString == null) { + reporter.error("Missing 'regex' string parameter") + return null + } + + val regex = try { + Regex(regexString) + } catch (t: Throwable) { + reporter.error("Invalid regex: '$regexString': ${t.message}") + return null + } + + val replacement = derivation.parameters["replacement"] as? String + if (replacement == null) { + reporter.error("Missing 'replacement' string parameter") + return null + } + + val maxLength = (derivation.parameters["maxLength"] as? Number)?.toInt() + return ReplacePropertyDerivation(regex, replacement, maxLength) + } + } +} diff --git a/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt b/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt new file mode 100644 index 000000000..1bb19f80a --- /dev/null +++ b/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt @@ -0,0 +1,43 @@ +package com.demonwav.mcdev.creator.custom + +import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.intellij.openapi.diagnostic.getOrLogException +import com.intellij.openapi.diagnostic.thisLogger + +class SelectPropertyDerivation( + val parents: List?, + val options: List, + val default: Any?, +) : PreparedDerivation { + + override fun derive(parentValues: List): Any? { + val properties = if (!parents.isNullOrEmpty()) { + parentValues.mapIndexed { i, value -> parents[i] to value }.toMap() + } else { + emptyMap() + } + for (option in options) { + if (TemplateEvaluator.condition(properties, option.condition).getOrLogException(thisLogger()) == true) { + return option.value + } + } + + return default + } + + companion object : PropertyDerivationFactory { + + override fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? { + if (derivation.select == null) { + reporter.error("Missing select options") + return null + } + + return SelectPropertyDerivation(derivation.parents, derivation.select, derivation.default) + } + } +} diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 7bf9fe033..8238b81d7 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -121,6 +121,8 @@ abstract class CreatorProperty( } } + setupDerivation(reporter, descriptor.derives) + fun collectParentValues(): List = parents.map { properties[it]!!.get() } @Suppress("UNCHECKED_CAST") @@ -147,6 +149,8 @@ abstract class CreatorProperty( } } + protected open fun setupDerivation(reporter: TemplateValidationReporter, derives: PropertyDerivation) {} + protected fun makeStorageKey(discriminator: String? = null): String { val base = "${javaClass.name}.property.${descriptor.name}.${descriptor.type}" if (discriminator == null) { @@ -156,6 +160,17 @@ abstract class CreatorProperty( return "$base.$discriminator" } + protected fun collectDerivationParents( + derives: PropertyDerivation, + reporter: TemplateValidationReporter + ): List?>? = derives.parents?.map { parentName -> + val property = properties[parentName] + if (property == null) { + reporter.error("Unknown parent property: $parentName") + } + return@map property + } + protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { row(descriptor.translatedLabel) { comboBox(options) diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 9256ac95a..9be1828a3 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -21,7 +21,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations +import com.demonwav.mcdev.creator.custom.PreparedDerivation import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.ReplacePropertyDerivation +import com.demonwav.mcdev.creator.custom.SelectPropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.ide.util.projectWizard.WizardContext @@ -41,6 +44,8 @@ class StringCreatorProperty( private var validationRegex: Regex? = null + private var derivation: PreparedDerivation? = null + override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" override fun serialize(value: String): String = value @@ -50,25 +55,10 @@ class StringCreatorProperty( override fun toStringProperty(graphProperty: GraphProperty) = graphProperty override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { - return when (derivation.method) { - "suggestSpongePluginId" -> suggestSpongePluginId(parentValues.first()) - null -> deriveSelectFirst(parentValues, derivation).toString() - else -> throw IllegalArgumentException("Unknown method derivation $derivation") - } - } - - private fun suggestSpongePluginId(projectName: Any?): String? { - if (projectName !is String) { - return null + if (this.derivation == null) { + throw IllegalStateException("This property has not been configured with a derivation") } - - val invalidModIdRegex = "[^a-z0-9-_]+".toRegex() - val sanitized = projectName.lowercase().replace(invalidModIdRegex, "_") - if (sanitized.length > 64) { - return sanitized.substring(0, 64) - } - - return sanitized + return this.derivation!!.derive(parentValues) } override fun setupProperty(reporter: TemplateValidationReporter) { @@ -84,6 +74,22 @@ class StringCreatorProperty( } } + override fun setupDerivation(reporter: TemplateValidationReporter, derives: PropertyDerivation) { + when (derives.method) { + "replace" -> { + val parents = collectDerivationParents(derives, reporter) + derivation = ReplacePropertyDerivation.create(reporter, parents, derives) + } + + null -> { + // No need to collect parent values for this one because it is not used + derivation = SelectPropertyDerivation.create(reporter, emptyList(), derives) + } + + else -> reporter.fatal("Unknown method derivation: $derivation") + } + } + override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.translatedLabel) { val textField = textField().bindText(this@StringCreatorProperty.toStringProperty(graphProperty)) From 3d6927a41bc6734352fac7c479f48868d9fcee7e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 11:32:43 +0200 Subject: [PATCH 088/118] Add missing licenses --- .../creator/custom/PropertyDerivation.kt | 20 +++++++++++++++++++ .../custom/ReplacePropertyDerivation.kt | 20 +++++++++++++++++++ .../custom/SelectPropertyDerivation.kt | 20 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/main/kotlin/creator/custom/PropertyDerivation.kt b/src/main/kotlin/creator/custom/PropertyDerivation.kt index a87864715..dff6cea3d 100644 --- a/src/main/kotlin/creator/custom/PropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/PropertyDerivation.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.types.CreatorProperty diff --git a/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt b/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt index 9f4c569ee..ea9ac7fd3 100644 --- a/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.types.CreatorProperty diff --git a/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt b/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt index 1bb19f80a..69aff6643 100644 --- a/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt @@ -1,3 +1,23 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package com.demonwav.mcdev.creator.custom import com.demonwav.mcdev.creator.custom.types.CreatorProperty From 0a769af968bab57a771c7c9b4544102a8f24d4ef Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 12:38:13 +0200 Subject: [PATCH 089/118] Remove unused imports --- src/main/kotlin/MinecraftConfigurable.kt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/kotlin/MinecraftConfigurable.kt b/src/main/kotlin/MinecraftConfigurable.kt index 98d829058..60f5a32ab 100644 --- a/src/main/kotlin/MinecraftConfigurable.kt +++ b/src/main/kotlin/MinecraftConfigurable.kt @@ -22,34 +22,22 @@ package com.demonwav.mcdev import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.asset.PlatformAssets -import com.demonwav.mcdev.creator.custom.providers.TemplateProvider import com.demonwav.mcdev.creator.custom.templateRepoTable import com.demonwav.mcdev.update.ConfigurePluginUpdatesDialog import com.intellij.ide.projectView.ProjectView import com.intellij.openapi.options.Configurable import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.ui.DialogPanel -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.ui.ComboBoxTableCellRenderer import com.intellij.ui.EnumComboBoxModel -import com.intellij.ui.ToolbarDecorator import com.intellij.ui.components.Label -import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.BottomGap import com.intellij.ui.dsl.builder.MutableProperty import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel -import com.intellij.ui.table.TableView import com.intellij.util.IconUtil -import com.intellij.util.ListWithSelection -import com.intellij.util.ui.ColumnInfo -import com.intellij.util.ui.ListTableModel -import com.intellij.util.ui.table.ComboBoxTableCellEditor import javax.swing.JComponent -import javax.swing.table.TableCellEditor -import javax.swing.table.TableCellRenderer import org.jetbrains.annotations.Nls class MinecraftConfigurable : Configurable { From d8b0103d47449eb5ef26f2af7d52924211e89b55 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 14:55:46 +0200 Subject: [PATCH 090/118] Get rid of builtin sponge specific derivation --- .../custom/types/IntegerCreatorProperty.kt | 10 ----- .../platform/sponge/util/SpongeVersions.kt | 42 ------------------- 2 files changed, 52 deletions(-) delete mode 100644 src/main/kotlin/platform/sponge/util/SpongeVersions.kt diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 4e7e650e5..8f0cb163e 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -23,7 +23,6 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion -import com.demonwav.mcdev.platform.sponge.util.SpongeVersions import com.demonwav.mcdev.util.MinecraftVersions import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext @@ -56,7 +55,6 @@ class IntegerCreatorProperty( override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { return when (derivation.method) { "recommendJavaVersionForMcVersion" -> recommendJavaVersionForMcVersion(parentValues[0]) - "recommendJavaVersionForSpongeApiVersion" -> recommendJavaVersionForSpongeApiVersion(parentValues[0]) null -> (deriveSelectFirst(parentValues, derivation) as Number).toInt() else -> throw IllegalArgumentException("Unknown method derivation $derivation") } @@ -74,14 +72,6 @@ class IntegerCreatorProperty( return 17 } - private fun recommendJavaVersionForSpongeApiVersion(from: Any?): Int { - if (from !is SemanticVersion) { - return 17 - } - - return SpongeVersions.requiredJavaVersion(from).ordinal - } - class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, diff --git a/src/main/kotlin/platform/sponge/util/SpongeVersions.kt b/src/main/kotlin/platform/sponge/util/SpongeVersions.kt deleted file mode 100644 index 8e699d7c7..000000000 --- a/src/main/kotlin/platform/sponge/util/SpongeVersions.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Minecraft Development for IntelliJ - * - * https://mcdev.io/ - * - * Copyright (C) 2024 minecraft-dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation, version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.demonwav.mcdev.platform.sponge.util - -import com.demonwav.mcdev.util.SemanticVersion -import com.intellij.openapi.projectRoots.JavaSdkVersion - -object SpongeVersions { - - val API8 = SemanticVersion.parse("8.0.0") to JavaSdkVersion.JDK_16 - val API9 = SemanticVersion.parse("9.0.0") to JavaSdkVersion.JDK_17 - val API10 = SemanticVersion.parse("10.0.0") to JavaSdkVersion.JDK_17 - val API11 = SemanticVersion.parse("11.0.0") to JavaSdkVersion.JDK_21 - - fun requiredJavaVersion(spongeApiVersion: SemanticVersion): JavaSdkVersion { - for ((api, java) in listOf(API8, API9, API10, API11).reversed()) { - if (spongeApiVersion >= api) { - return java - } - } - - return JavaSdkVersion.JDK_17 - } -} From fc2d756fb03a89d3f9aed6f3930af0ca8f1330b4 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 23:10:11 +0200 Subject: [PATCH 091/118] Rework how all derivations work, with validation now! --- .../creator/custom/CustomPlatformStep.kt | 3 +- ...ractVersionMajorMinorPropertyDerivation.kt | 66 +++++++++++++++++ .../{ => derivation}/PropertyDerivation.kt | 4 +- ...vaVersionForMcVersionPropertyDerivation.kt | 74 +++++++++++++++++++ .../ReplacePropertyDerivation.kt | 6 +- .../SelectPropertyDerivation.kt | 6 +- .../SuggestClassNamePropertyDerivation.kt | 68 +++++++++++++++++ .../ArchitecturyVersionsCreatorProperty.kt | 2 +- .../custom/types/BooleanCreatorProperty.kt | 2 +- .../BuildSystemCoordinatesCreatorProperty.kt | 2 +- .../custom/types/ClassFqnCreatorProperty.kt | 28 +++---- .../creator/custom/types/CreatorProperty.kt | 66 +++++++++++------ .../custom/types/ExternalCreatorProperty.kt | 3 +- .../types/FabricVersionsCreatorProperty.kt | 2 +- .../types/ForgeVersionsCreatorProperty.kt | 2 +- .../types/InlineStringListCreatorProperty.kt | 2 +- .../custom/types/IntegerCreatorProperty.kt | 36 ++++----- .../custom/types/JdkCreatorProperty.kt | 2 +- .../custom/types/LicenseCreatorProperty.kt | 2 +- .../types/NeoForgeVersionsCreatorProperty.kt | 2 +- .../custom/types/ParchmentCreatorProperty.kt | 7 +- .../types/SemanticVersionCreatorProperty.kt | 36 +++------ .../custom/types/SimpleCreatorProperty.kt | 5 +- .../custom/types/StringCreatorProperty.kt | 42 +++++------ .../architectury/ArchitecturyVersion.kt | 10 +-- 25 files changed, 347 insertions(+), 131 deletions(-) create mode 100644 src/main/kotlin/creator/custom/derivation/ExtractVersionMajorMinorPropertyDerivation.kt rename src/main/kotlin/creator/custom/{ => derivation}/PropertyDerivation.kt (85%) create mode 100644 src/main/kotlin/creator/custom/derivation/RecommendJavaVersionForMcVersionPropertyDerivation.kt rename src/main/kotlin/creator/custom/{ => derivation}/ReplacePropertyDerivation.kt (92%) rename src/main/kotlin/creator/custom/{ => derivation}/SelectPropertyDerivation.kt (87%) create mode 100644 src/main/kotlin/creator/custom/derivation/SuggestClassNamePropertyDerivation.kt diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 059ebd153..12bd21ab9 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -319,7 +319,8 @@ class CustomPlatformStep( properties["PROJECT_NAME"] = ExternalCreatorProperty( graph = propertyGraph, properties = properties, - graphProperty = baseData.nameProperty + graphProperty = baseData.nameProperty, + valueType = String::class.java ) placeholder.component = panel { diff --git a/src/main/kotlin/creator/custom/derivation/ExtractVersionMajorMinorPropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/ExtractVersionMajorMinorPropertyDerivation.kt new file mode 100644 index 000000000..186117050 --- /dev/null +++ b/src/main/kotlin/creator/custom/derivation/ExtractVersionMajorMinorPropertyDerivation.kt @@ -0,0 +1,66 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.derivation + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.demonwav.mcdev.util.SemanticVersion + +class ExtractVersionMajorMinorPropertyDerivation : PreparedDerivation { + + override fun derive(parentValues: List): Any? { + val from = parentValues[0] as SemanticVersion + if (from.parts.size < 2) { + return SemanticVersion(emptyList()) + } + + val (part1, part2) = from.parts + if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && + part2 is SemanticVersion.Companion.VersionPart.ReleasePart + ) { + return SemanticVersion(listOf(part1, part2)) + } + + return SemanticVersion(emptyList()) + } + + companion object : PropertyDerivationFactory { + + override fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? { + if (parents.isNullOrEmpty()) { + reporter.error("Expected a parent") + return null + } + + if (!parents[0]!!.acceptsType(SemanticVersion::class.java)) { + reporter.error("First parent must produce a semantic version") + return null + } + + return ExtractVersionMajorMinorPropertyDerivation() + } + } +} diff --git a/src/main/kotlin/creator/custom/PropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/PropertyDerivation.kt similarity index 85% rename from src/main/kotlin/creator/custom/PropertyDerivation.kt rename to src/main/kotlin/creator/custom/derivation/PropertyDerivation.kt index dff6cea3d..8d1aaf1dd 100644 --- a/src/main/kotlin/creator/custom/PropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/derivation/PropertyDerivation.kt @@ -18,8 +18,10 @@ * along with this program. If not, see . */ -package com.demonwav.mcdev.creator.custom +package com.demonwav.mcdev.creator.custom.derivation +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.types.CreatorProperty fun interface PreparedDerivation { diff --git a/src/main/kotlin/creator/custom/derivation/RecommendJavaVersionForMcVersionPropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/RecommendJavaVersionForMcVersionPropertyDerivation.kt new file mode 100644 index 000000000..c762c09b5 --- /dev/null +++ b/src/main/kotlin/creator/custom/derivation/RecommendJavaVersionForMcVersionPropertyDerivation.kt @@ -0,0 +1,74 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.derivation + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion +import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.demonwav.mcdev.util.MinecraftVersions +import com.demonwav.mcdev.util.SemanticVersion + +class RecommendJavaVersionForMcVersionPropertyDerivation(val default: Int) : PreparedDerivation { + + override fun derive(parentValues: List): Any? { + val mcVersion: SemanticVersion = when (val version = parentValues[0]) { + is SemanticVersion -> version + is HasMinecraftVersion -> version.minecraftVersion + else -> return default + } + return MinecraftVersions.requiredJavaVersion(mcVersion).ordinal + } + + companion object : PropertyDerivationFactory { + + override fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? { + if (parents.isNullOrEmpty()) { + reporter.error("Expected one parent") + return null + } + + if (parents.size > 1) { + reporter.warn("More than one parent defined") + } + + val parentValue = parents[0]!! + if (!parentValue.acceptsType(SemanticVersion::class.java) && + !parentValue.acceptsType(HasMinecraftVersion::class.java) + ) { + reporter.error("Parent must produce a semantic version or a value that has a Minecraft version") + return null + } + + val default = (derivation.default as? Number)?.toInt() + if (default == null) { + reporter.error("Default value is required and must be an integer") + return null + } + + return RecommendJavaVersionForMcVersionPropertyDerivation(default) + } + } +} diff --git a/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/ReplacePropertyDerivation.kt similarity index 92% rename from src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt rename to src/main/kotlin/creator/custom/derivation/ReplacePropertyDerivation.kt index ea9ac7fd3..c3688f9ad 100644 --- a/src/main/kotlin/creator/custom/ReplacePropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/derivation/ReplacePropertyDerivation.kt @@ -18,8 +18,10 @@ * along with this program. If not, see . */ -package com.demonwav.mcdev.creator.custom +package com.demonwav.mcdev.creator.custom.derivation +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.types.CreatorProperty class ReplacePropertyDerivation( @@ -61,7 +63,7 @@ class ReplacePropertyDerivation( reporter.warn("More than one parent defined") } - if (parents.first()?.get() !is String) { + if (!parents[0]!!.acceptsType(String::class.java)) { reporter.error("Parent property must produce a string value") return null } diff --git a/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/SelectPropertyDerivation.kt similarity index 87% rename from src/main/kotlin/creator/custom/SelectPropertyDerivation.kt rename to src/main/kotlin/creator/custom/derivation/SelectPropertyDerivation.kt index 69aff6643..88444bb5d 100644 --- a/src/main/kotlin/creator/custom/SelectPropertyDerivation.kt +++ b/src/main/kotlin/creator/custom/derivation/SelectPropertyDerivation.kt @@ -18,8 +18,12 @@ * along with this program. If not, see . */ -package com.demonwav.mcdev.creator.custom +package com.demonwav.mcdev.creator.custom.derivation +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.PropertyDerivationSelect +import com.demonwav.mcdev.creator.custom.TemplateEvaluator +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.types.CreatorProperty import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.thisLogger diff --git a/src/main/kotlin/creator/custom/derivation/SuggestClassNamePropertyDerivation.kt b/src/main/kotlin/creator/custom/derivation/SuggestClassNamePropertyDerivation.kt new file mode 100644 index 000000000..8362a7b4a --- /dev/null +++ b/src/main/kotlin/creator/custom/derivation/SuggestClassNamePropertyDerivation.kt @@ -0,0 +1,68 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.creator.custom.derivation + +import com.demonwav.mcdev.creator.custom.PropertyDerivation +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates +import com.demonwav.mcdev.creator.custom.model.ClassFqn +import com.demonwav.mcdev.creator.custom.types.CreatorProperty +import com.demonwav.mcdev.util.capitalize +import com.demonwav.mcdev.util.decapitalize + +class SuggestClassNamePropertyDerivation : PreparedDerivation { + + override fun derive(parentValues: List): Any? { + val coords = parentValues[0] as BuildSystemCoordinates + val name = parentValues[1] as String + return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}") + } + + companion object : PropertyDerivationFactory { + + override fun create( + reporter: TemplateValidationReporter, + parents: List?>?, + derivation: PropertyDerivation + ): PreparedDerivation? { + if (parents == null || parents.size < 2) { + reporter.error("Expected 2 parents") + return null + } + + if (parents.size > 2) { + reporter.warn("More than two parents defined") + } + + if (!parents[0]!!.acceptsType(BuildSystemCoordinates::class.java)) { + reporter.error("First parent must produce a build system coordinates") + return null + } + + if (!parents[1]!!.acceptsType(String::class.java)) { + reporter.error("Second parent must produce a string value") + return null + } + + return SuggestClassNamePropertyDerivation() + } + } +} diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt index bb062b929..a623aa655 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt @@ -54,7 +54,7 @@ class ArchitecturyVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, ArchitecturyVersionsModel::class.java) { private val emptyVersion = SemanticVersion.release() private val emptyValue = ArchitecturyVersionsModel( diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index a649f3773..e0814ec99 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -33,7 +33,7 @@ class BooleanCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, Boolean::class.java) { override fun createDefaultValue(raw: Any?): Boolean = raw as? Boolean ?: false diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index 4659c28fc..beab81ac0 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -48,7 +48,7 @@ class BuildSystemCoordinatesCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, BuildSystemCoordinates::class.java) { private val default = createDefaultValue(descriptor.default) diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 0e782a502..56d8b994c 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -23,10 +23,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor -import com.demonwav.mcdev.creator.custom.model.BuildSystemCoordinates +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.SuggestClassNamePropertyDerivation import com.demonwav.mcdev.creator.custom.model.ClassFqn -import com.demonwav.mcdev.util.capitalize -import com.demonwav.mcdev.util.decapitalize import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.COLUMNS_LARGE @@ -39,7 +39,7 @@ class ClassFqnCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, ClassFqn::class.java) { override fun createDefaultValue(raw: Any?): ClassFqn = ClassFqn(raw as? String ?: "") @@ -56,20 +56,16 @@ class ClassFqnCreatorProperty( }.visible(descriptor.hidden != true) } - override fun derive(parentValues: List, derivation: PropertyDerivation): ClassFqn { - return when (derivation.method) { - "suggestClassName" -> suggestClassName(parentValues) - null -> ClassFqn(deriveSelectFirst(parentValues, derivation).toString()) - else -> throw IllegalArgumentException("Unknown method derivation $derivation") + override fun setupDerivation( + reporter: TemplateValidationReporter, + derives: PropertyDerivation + ): PreparedDerivation? = when (derives.method) { + "suggestClassName" -> { + val parents = collectDerivationParents(reporter) + SuggestClassNamePropertyDerivation.create(reporter, parents, derives) } - } - private fun suggestClassName(parentValues: List): ClassFqn { - val coords = parentValues.getOrNull(0) as? BuildSystemCoordinates - ?: throw RuntimeException("Expected parent 0 to be a build system coordinates") - val name = parentValues.getOrNull(1) as? String - ?: throw RuntimeException("Expected parent 1 to be a string") - return ClassFqn("${coords.groupId}.${name.decapitalize()}.${name.capitalize()}") + else -> null } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 8238b81d7..7ada590e4 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -24,6 +24,8 @@ import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.thisLogger @@ -39,8 +41,11 @@ import com.intellij.ui.dsl.builder.bindItem abstract class CreatorProperty( val descriptor: TemplatePropertyDescriptor, val graph: PropertyGraph, - protected val properties: Map> + protected val properties: Map>, + val valueType: Class ) { + private var derivation: PreparedDerivation? = null + abstract val graphProperty: GraphProperty abstract fun createDefaultValue(raw: Any?): T @@ -64,6 +69,8 @@ abstract class CreatorProperty( return value } + fun acceptsType(type: Class<*>): Boolean = type.isAssignableFrom(valueType) + /** * Produces a new value based on the provided [parentValues] and the template-defined [derivation] configuration. * @@ -74,15 +81,21 @@ abstract class CreatorProperty( * * @see GraphProperty.dependsOn */ - open fun derive(parentValues: List, derivation: PropertyDerivation): Any? { - if (derivation.select != null) { - return deriveSelectFirst(parentValues, derivation) + open fun derive(parentValues: List?, derivation: PropertyDerivation): Any? { + if (this.derivation == null) { + throw IllegalStateException("This property has not been configured with a derivation") + } + + val result = this.derivation!!.derive(parentValues.orEmpty()) + if (this.derivation is SelectPropertyDerivation) { + return convertSelectDerivationResult(result) } - thisLogger().error("This type doesn't support derivation") - return graphProperty.get() + return result } + protected open fun convertSelectDerivationResult(original: Any?): Any? = original + fun deriveSelectFirst(parentValues: List, derivation: PropertyDerivation): Any? { val properties = parentValues.mapIndexed { i, value -> derivation.parents!![i] to value }.toMap() for (select in derivation.select ?: emptyList()) { @@ -121,17 +134,18 @@ abstract class CreatorProperty( } } - setupDerivation(reporter, descriptor.derives) - - fun collectParentValues(): List = parents.map { properties[it]!!.get() } + derivation = setupDerivation(reporter, descriptor.derives) + if (derivation == null) { + reporter.fatal("Unknown method derivation: ${descriptor.derives}") + } @Suppress("UNCHECKED_CAST") - graphProperty.set(derive(collectParentValues(), descriptor.derives) as T) + graphProperty.set(derive(collectDerivationParentValues(reporter), descriptor.derives) as T) for (parent in parents) { val parentProperty = properties[parent]!! graphProperty.dependsOn(parentProperty.graphProperty, descriptor.derives.whenModified != false) { @Suppress("UNCHECKED_CAST") - derive(collectParentValues(), descriptor.derives) as T + derive(collectDerivationParentValues(), descriptor.derives) as T } } } @@ -149,7 +163,10 @@ abstract class CreatorProperty( } } - protected open fun setupDerivation(reporter: TemplateValidationReporter, derives: PropertyDerivation) {} + protected open fun setupDerivation( + reporter: TemplateValidationReporter, + derives: PropertyDerivation + ): PreparedDerivation? = null protected fun makeStorageKey(discriminator: String? = null): String { val base = "${javaClass.name}.property.${descriptor.name}.${descriptor.type}" @@ -160,16 +177,23 @@ abstract class CreatorProperty( return "$base.$discriminator" } - protected fun collectDerivationParents( - derives: PropertyDerivation, - reporter: TemplateValidationReporter - ): List?>? = derives.parents?.map { parentName -> - val property = properties[parentName] - if (property == null) { - reporter.error("Unknown parent property: $parentName") + protected fun collectDerivationParents(reporter: TemplateValidationReporter? = null): List?>? = + descriptor.derives?.parents?.map { parentName -> + val property = properties[parentName] + if (property == null) { + reporter?.error("Unknown parent property: $parentName") + } + return@map property + } + + protected fun collectDerivationParentValues(reporter: TemplateValidationReporter? = null): List? = + descriptor.derives?.parents?.map { parentName -> + val property = properties[parentName] + if (property == null) { + reporter?.error("Unknown parent property: $parentName") + } + return@map property?.get() } - return@map property - } protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { row(descriptor.translatedLabel) { diff --git a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt index ec38b3993..b51b0e58c 100644 --- a/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ExternalCreatorProperty.kt @@ -32,7 +32,8 @@ class ExternalCreatorProperty( graph: PropertyGraph, properties: Map>, override val graphProperty: GraphProperty, -) : CreatorProperty(descriptor, graph, properties) { + valueType: Class, +) : CreatorProperty(descriptor, graph, properties, valueType) { override fun setupProperty(reporter: TemplateValidationReporter) = Unit diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt index 39908b521..51ba259ee 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt @@ -53,7 +53,7 @@ class FabricVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, FabricVersionsModel::class.java) { private val emptyVersion = SemanticVersion.release() private val emptyValue = FabricVersionsModel( diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt index ffb7e264b..1193feea0 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt @@ -48,7 +48,7 @@ class ForgeVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, ForgeVersions::class.java) { private val emptyVersion = SemanticVersion.release() diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt index b713d8639..cc24b97af 100644 --- a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -33,7 +33,7 @@ class InlineStringListCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, StringList::class.java) { override fun createDefaultValue(raw: Any?): StringList = deserialize(raw as? String ?: "") diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index 8f0cb163e..de108c310 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -22,9 +22,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor -import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion -import com.demonwav.mcdev.util.MinecraftVersions -import com.demonwav.mcdev.util.SemanticVersion +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.RecommendJavaVersionForMcVersionPropertyDerivation +import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.dsl.builder.COLUMNS_LARGE @@ -36,7 +37,7 @@ class IntegerCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, Int::class.java) { override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 @@ -44,6 +45,8 @@ class IntegerCreatorProperty( override fun deserialize(string: String): Int = string.toIntOrNull() ?: 0 + override fun convertSelectDerivationResult(original: Any?): Any? = (original as? Number)?.toInt() + override fun buildSimpleUi(panel: Panel, context: WizardContext) { panel.row(descriptor.translatedLabel) { this.intTextField().bindIntText(graphProperty) @@ -52,24 +55,21 @@ class IntegerCreatorProperty( }.visible(descriptor.hidden != true) } - override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { - return when (derivation.method) { - "recommendJavaVersionForMcVersion" -> recommendJavaVersionForMcVersion(parentValues[0]) - null -> (deriveSelectFirst(parentValues, derivation) as Number).toInt() - else -> throw IllegalArgumentException("Unknown method derivation $derivation") - } - } - - private fun recommendJavaVersionForMcVersion(from: Any?): Int { - if (from is SemanticVersion) { - return MinecraftVersions.requiredJavaVersion(from).ordinal + override fun setupDerivation( + reporter: TemplateValidationReporter, + derives: PropertyDerivation + ): PreparedDerivation? = when (derives.method) { + "recommendJavaVersionForMcVersion" -> { + val parents = collectDerivationParents(reporter) + RecommendJavaVersionForMcVersionPropertyDerivation.create(reporter, parents, derives) } - if (from is HasMinecraftVersion) { - return recommendJavaVersionForMcVersion(from.minecraftVersion) + null -> { + // No need to collect parent values for this one because it is not used + SelectPropertyDerivation.create(reporter, emptyList(), derives) } - return 17 + else -> null } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt index b09efeb6c..02608ed5f 100644 --- a/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/JdkCreatorProperty.kt @@ -35,7 +35,7 @@ class JdkCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, CreatorJdk::class.java) { private lateinit var jdkComboBox: JdkComboBoxWithPreference diff --git a/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt b/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt index 2e0a61d68..fc6ae05df 100644 --- a/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/LicenseCreatorProperty.kt @@ -37,7 +37,7 @@ class LicenseCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, LicenseData::class.java) { override val graphProperty: GraphProperty = graph.property(createDefaultValue(descriptor.default)) diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index 539f10234..760970fb2 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -48,7 +48,7 @@ class NeoForgeVersionsCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, NeoForgeVersions::class.java) { private val emptyVersion = SemanticVersion.release() diff --git a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt index d10529205..6bb379ab1 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.ParchmentVersion +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.HasMinecraftVersion @@ -45,7 +46,7 @@ class ParchmentCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : CreatorProperty(descriptor, graph, properties) { +) : CreatorProperty(descriptor, graph, properties, ParchmentVersions::class.java) { private val emptyVersion = SemanticVersion.release() @@ -100,11 +101,15 @@ class ParchmentCreatorProperty( comboBox(mcVersionsModel) .bindItem(mcVersionProperty) .enabledIf(useParchmentProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } comboBox(versionsModel) .bindItem(versionProperty) .enabledIf(useParchmentProperty) + .validationOnInput(BuiltinValidations.nonEmptyVersion) + .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 991da2543..62873e9ca 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -22,6 +22,9 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.PropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.derivation.ExtractVersionMajorMinorPropertyDerivation +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph @@ -34,7 +37,7 @@ open class SemanticVersionCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, SemanticVersion::class.java) { override fun createDefaultValue(raw: Any?): SemanticVersion = SemanticVersion.tryParse(raw as? String ?: "") ?: SemanticVersion(emptyList()) @@ -52,31 +55,16 @@ open class SemanticVersionCreatorProperty( }.visible(descriptor.hidden != true) } - override fun derive(parentValues: List, derivation: PropertyDerivation): Any { - return when (derivation.method) { - "extractVersionMajorMinor" -> extractVersionMajorMinor(parentValues[0]) - null -> SemanticVersion.parse(deriveSelectFirst(parentValues, derivation).toString()) - else -> throw IllegalArgumentException("Unknown method derivation $derivation") - } - } - - private fun extractVersionMajorMinor(from: Any?): SemanticVersion { - if (from !is SemanticVersion) { - return SemanticVersion(emptyList()) - } - - if (from.parts.size < 2) { - return SemanticVersion(emptyList()) - } - - val (part1, part2) = from.parts - if (part1 is SemanticVersion.Companion.VersionPart.ReleasePart && - part2 is SemanticVersion.Companion.VersionPart.ReleasePart - ) { - return SemanticVersion(listOf(part1, part2)) + override fun setupDerivation( + reporter: TemplateValidationReporter, + derives: PropertyDerivation + ): PreparedDerivation? = when (derives.method) { + "extractVersionMajorMinor" -> { + val parents = collectDerivationParents(reporter) + ExtractVersionMajorMinorPropertyDerivation.create(reporter, parents, derives) } - return SemanticVersion(emptyList()) + else -> null } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 2716033a9..5d9e1ff02 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -34,8 +34,9 @@ import javax.swing.JList abstract class SimpleCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, - properties: Map> -) : CreatorProperty(descriptor, graph, properties) { + properties: Map>, + valueType: Class +) : CreatorProperty(descriptor, graph, properties, valueType) { private val options: Map? = makeOptionsList() diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 9be1828a3..1ce12bebf 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -21,10 +21,10 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations -import com.demonwav.mcdev.creator.custom.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation import com.demonwav.mcdev.creator.custom.PropertyDerivation -import com.demonwav.mcdev.creator.custom.ReplacePropertyDerivation -import com.demonwav.mcdev.creator.custom.SelectPropertyDerivation +import com.demonwav.mcdev.creator.custom.derivation.ReplacePropertyDerivation +import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.intellij.ide.util.projectWizard.WizardContext @@ -40,12 +40,10 @@ class StringCreatorProperty( descriptor: TemplatePropertyDescriptor, graph: PropertyGraph, properties: Map> -) : SimpleCreatorProperty(descriptor, graph, properties) { +) : SimpleCreatorProperty(descriptor, graph, properties, String::class.java) { private var validationRegex: Regex? = null - private var derivation: PreparedDerivation? = null - override fun createDefaultValue(raw: Any?): String = raw as? String ?: "" override fun serialize(value: String): String = value @@ -54,13 +52,6 @@ class StringCreatorProperty( override fun toStringProperty(graphProperty: GraphProperty) = graphProperty - override fun derive(parentValues: List, derivation: PropertyDerivation): Any? { - if (this.derivation == null) { - throw IllegalStateException("This property has not been configured with a derivation") - } - return this.derivation!!.derive(parentValues) - } - override fun setupProperty(reporter: TemplateValidationReporter) { super.setupProperty(reporter) @@ -74,20 +65,21 @@ class StringCreatorProperty( } } - override fun setupDerivation(reporter: TemplateValidationReporter, derives: PropertyDerivation) { - when (derives.method) { - "replace" -> { - val parents = collectDerivationParents(derives, reporter) - derivation = ReplacePropertyDerivation.create(reporter, parents, derives) - } - - null -> { - // No need to collect parent values for this one because it is not used - derivation = SelectPropertyDerivation.create(reporter, emptyList(), derives) - } + override fun setupDerivation( + reporter: TemplateValidationReporter, + derives: PropertyDerivation + ): PreparedDerivation? = when (derives.method) { + "replace" -> { + val parents = collectDerivationParents(reporter) + ReplacePropertyDerivation.create(reporter, parents, derives) + } - else -> reporter.fatal("Unknown method derivation: $derivation") + null -> { + // No need to collect parent values for this one because it is not used + SelectPropertyDerivation.create(reporter, emptyList(), derives) } + + else -> null } override fun buildSimpleUi(panel: Panel, context: WizardContext) { diff --git a/src/main/kotlin/platform/architectury/ArchitecturyVersion.kt b/src/main/kotlin/platform/architectury/ArchitecturyVersion.kt index dae07b3d7..e5df5ce13 100644 --- a/src/main/kotlin/platform/architectury/ArchitecturyVersion.kt +++ b/src/main/kotlin/platform/architectury/ArchitecturyVersion.kt @@ -29,21 +29,13 @@ import com.github.kittinunf.fuel.core.requests.suspendable import com.github.kittinunf.fuel.coroutines.awaitString import com.google.gson.Gson import com.google.gson.annotations.SerializedName -import java.io.IOException class ArchitecturyVersion private constructor( val versions: Map>, ) { fun getArchitecturyVersions(mcVersion: SemanticVersion): List { - return try { - val architecturyVersions = versions[mcVersion] - ?: throw IOException("Could not find any architectury versions for $mcVersion") - architecturyVersions.take(50) - } catch (e: IOException) { - e.printStackTrace() - emptyList() - } + return versions[mcVersion].orEmpty().take(50) } data class ModrinthVersionApi( From d6c6aafd7160780a39cbf3c0dbe0534d7fcfbfaf Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 16 Jun 2024 23:18:08 +0200 Subject: [PATCH 092/118] Fix imports, again --- .../kotlin/creator/custom/types/StringCreatorProperty.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index 1ce12bebf..f76625052 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -21,12 +21,12 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.custom.BuiltinValidations -import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation import com.demonwav.mcdev.creator.custom.PropertyDerivation -import com.demonwav.mcdev.creator.custom.derivation.ReplacePropertyDerivation -import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter +import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.ReplacePropertyDerivation +import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph From 119636b2a229480becc3ef6bd453ac3489615bf2 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 17 Jun 2024 23:00:16 +0200 Subject: [PATCH 093/118] MavenArtifactVersion should load versions in setupProperty --- .../MavenArtifactVersionCreatorProperty.kt | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index 9c5656afb..726f5ad5a 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -22,6 +22,7 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor +import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty @@ -42,30 +43,12 @@ class MavenArtifactVersionCreatorProperty( properties: Map> ) : SemanticVersionCreatorProperty(descriptor, graph, properties) { - val sourceUrl: String - get() = descriptor.parameters!!["sourceUrl"] as String + lateinit var sourceUrl: String override val graphProperty: GraphProperty = graph.property(SemanticVersion(emptyList())) private val versionsProperty = graph.property>(emptyList()) private val loadingVersionsProperty = graph.property(true) - init { - application.executeOnPooledThread { - runBlocking { - val versions = collectMavenVersions(sourceUrl) - .asSequence() - .mapNotNull(SemanticVersion::tryParse) - .sortedDescending() - .take(descriptor.limit ?: 50) - .toList() - withContext(Dispatchers.Swing) { - versionsProperty.set(versions) - loadingVersionsProperty.set(false) - } - } - } - } - override fun buildUi(panel: Panel, context: WizardContext) { panel.row(descriptor.translatedLabel) { val combobox = comboBox(versionsProperty.get()) @@ -85,6 +68,33 @@ class MavenArtifactVersionCreatorProperty( }.visible(descriptor.hidden != true) } + override fun setupProperty(reporter: TemplateValidationReporter) { + super.setupProperty(reporter) + + val url = descriptor.parameters?.get("sourceUrl") as? String + if (url == null) { + reporter.error("Expected string parameter 'sourceUrl'") + return + } + + sourceUrl = url + + application.executeOnPooledThread { + runBlocking { + val versions = collectMavenVersions(sourceUrl) + .asSequence() + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + .take(descriptor.limit ?: 50) + .toList() + withContext(Dispatchers.Swing) { + versionsProperty.set(versions) + loadingVersionsProperty.set(false) + } + } + } + } + class Factory : CreatorPropertyFactory { override fun create( From ba5c7e612898504b4718be872df2debe22c84ac3 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 18 Jun 2024 01:38:30 +0200 Subject: [PATCH 094/118] Invert hidden -> visible and added custom visibility conditions --- .../creator/custom/CustomPlatformStep.kt | 2 +- .../creator/custom/TemplateDescriptor.kt | 2 +- .../custom/types/BooleanCreatorProperty.kt | 2 +- .../custom/types/ClassFqnCreatorProperty.kt | 2 +- .../creator/custom/types/CreatorProperty.kt | 80 ++++++++++++++++++- .../types/InlineStringListCreatorProperty.kt | 2 +- .../custom/types/IntegerCreatorProperty.kt | 2 +- .../MavenArtifactVersionCreatorProperty.kt | 2 +- .../types/SemanticVersionCreatorProperty.kt | 2 +- .../custom/types/SimpleCreatorProperty.kt | 2 +- .../custom/types/StringCreatorProperty.kt | 2 +- 11 files changed, 89 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 12bd21ab9..4c69223cc 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -427,7 +427,7 @@ class CustomPlatformStep( properties[descriptor.name] = prop - if (descriptor.hidden == true) { + if (descriptor.visible == false) { return null } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 1b5c895e6..aa715828c 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -50,7 +50,7 @@ data class TemplatePropertyDescriptor( val forceDropdown: Boolean? = null, val groupProperties: List? = null, val remember: Boolean? = null, - val hidden: Boolean? = null, + val visible: Any? = null, val editable: Boolean? = null, val collapsible: Boolean? = null, val warning: String? = null, diff --git a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt index e0814ec99..57072d519 100644 --- a/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BooleanCreatorProperty.kt @@ -54,7 +54,7 @@ class BooleanCreatorProperty( this.checkBox(label.removeSuffix(":").trim()) .bindSelected(graphProperty) .enabled(descriptor.editable != false) - }.visible(descriptor.hidden != true) + }.propertyVisibility() } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt index 56d8b994c..5ee2470ad 100644 --- a/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ClassFqnCreatorProperty.kt @@ -53,7 +53,7 @@ class ClassFqnCreatorProperty( .columns(COLUMNS_LARGE) .textValidation(BuiltinValidations.validClassFqn) .enabled(descriptor.editable != false) - }.visible(descriptor.hidden != true) + }.propertyVisibility() } override fun setupDerivation( diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 7ada590e4..77188ac7c 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -36,6 +36,7 @@ import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel +import com.intellij.ui.dsl.builder.Row import com.intellij.ui.dsl.builder.bindItem abstract class CreatorProperty( @@ -45,6 +46,7 @@ abstract class CreatorProperty( val valueType: Class ) { private var derivation: PreparedDerivation? = null + private lateinit var visibleProperty: GraphProperty abstract val graphProperty: GraphProperty @@ -125,6 +127,8 @@ abstract class CreatorProperty( toStringProperty(graphProperty).bindStorage(makeStorageKey()) } + visibleProperty = setupVisibleProperty(reporter, descriptor.visible) + if (descriptor.derives != null) { val parents = descriptor.derives.parents ?: return reporter.error("No parents specified in derivation") @@ -195,12 +199,86 @@ abstract class CreatorProperty( return@map property?.get() } + protected fun Row.propertyVisibility(): Row = this.visibleIf(visibleProperty) + + private fun setupVisibleProperty( + reporter: TemplateValidationReporter, + visibility: Any? + ): GraphProperty { + val prop = graph.property(true) + if (visibility == null || visibility is Boolean) { + prop.set(visibility != false) + return prop + } + + if (visibility !is Map<*, *>) { + reporter.error("Visibility can only be a boolean or an object") + return prop + } + + var dependsOn = visibility["dependsOn"] + if (dependsOn !is String && (dependsOn !is List<*> || dependsOn.any { it !is String })) { + reporter.error( + "Expected 'visible' to have a 'dependsOn' value that is either a string or a list of strings" + ) + return prop + } + + val dependenciesNames = when (dependsOn) { + is String -> setOf(dependsOn) + is Collection<*> -> dependsOn.filterIsInstance().toSet() + else -> throw IllegalStateException("Should not be reached") + } + val dependencies = dependenciesNames.mapNotNull { + val dependency = this.properties[it] + if (dependency == null) { + reporter.error("Visibility dependency '$it' does not exist") + } + dependency + } + if (dependencies.size != dependenciesNames.size) { + // Errors have already been reported + return prop + } + + val condition = visibility["condition"] + if (condition !is String) { + reporter.error("Expected 'visible' to have a 'condition' string") + return prop + } + + var didInitialUpdate = false + val update: () -> Boolean = { + val conditionProperties = dependencies.associate { prop -> prop.descriptor.name to prop.get() } + val result = TemplateEvaluator.condition(conditionProperties, condition) + val exception = result.exceptionOrNull() + if (exception != null) { + if (!didInitialUpdate) { + didInitialUpdate = true + reporter.error("Failed to compute initial visibility: ${exception.message}") + thisLogger().info("Failed to compute initial visibility: ${exception.message}", exception) + } else { + thisLogger().error("Failed to compute initial visibility: ${exception.message}", exception) + } + } + + result.getOrDefault(true) + } + + prop.set(update()) + for (dependency in dependencies) { + prop.dependsOn(dependency.graphProperty, deleteWhenModified = false, update) + } + + return prop + } + protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { row(descriptor.translatedLabel) { comboBox(options) .bindItem(graphProp) .enabled(descriptor.editable != false) .also { ComboboxSpeedSearch.installOn(it.component) } - }.visible(descriptor.hidden != true) + }.propertyVisibility() } } diff --git a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt index cc24b97af..67e931edf 100644 --- a/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/InlineStringListCreatorProperty.kt @@ -49,7 +49,7 @@ class InlineStringListCreatorProperty( this.textField().bindText(this@InlineStringListCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) - }.visible(descriptor.hidden != true) + }.propertyVisibility() } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index de108c310..f82ebc623 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -52,7 +52,7 @@ class IntegerCreatorProperty( this.intTextField().bindIntText(graphProperty) .columns(COLUMNS_LARGE) .enabled(descriptor.editable != false) - }.visible(descriptor.hidden != true) + }.propertyVisibility() } override fun setupDerivation( diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index 726f5ad5a..f06a51e64 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -65,7 +65,7 @@ class MavenArtifactVersionCreatorProperty( combobox.component.addItem(version) } } - }.visible(descriptor.hidden != true) + }.propertyVisibility() } override fun setupProperty(reporter: TemplateValidationReporter) { diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index 62873e9ca..f45b2a467 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -52,7 +52,7 @@ open class SemanticVersionCreatorProperty( this.textField().bindText(this@SemanticVersionCreatorProperty.toStringProperty(graphProperty)) .columns(COLUMNS_SHORT) .enabled(descriptor.editable != false) - }.visible(descriptor.hidden != true) + }.propertyVisibility() } override fun setupDerivation( diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 5d9e1ff02..0e814d28f 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -92,7 +92,7 @@ abstract class SimpleCreatorProperty( .enabled(descriptor.editable != false) .maxButtonsCount(4) } - }.visible(descriptor.hidden != true) + }.propertyVisibility() } else { buildSimpleUi(panel, context) } diff --git a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt index f76625052..31582bcc7 100644 --- a/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/StringCreatorProperty.kt @@ -90,7 +90,7 @@ class StringCreatorProperty( if (validationRegex != null) { textField.textValidation(BuiltinValidations.byRegex(validationRegex!!)) } - }.visible(descriptor.hidden != true) + }.propertyVisibility() } class Factory : CreatorPropertyFactory { From 31ffbbf7f542c16729c288fad0c45ef3e0cd10d8 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 18 Jun 2024 01:49:07 +0200 Subject: [PATCH 095/118] Add build coords default group and version --- .../custom/types/BuildSystemCoordinatesCreatorProperty.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt index beab81ac0..2d70ef5cc 100644 --- a/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/BuildSystemCoordinatesCreatorProperty.kt @@ -64,7 +64,7 @@ class BuildSystemCoordinatesCreatorProperty( return deserialize(str) } - private fun createDefaultValue() = BuildSystemCoordinates("", "", "") + private fun createDefaultValue() = BuildSystemCoordinates("org.example", "", "1.0-SNAPSHOT") override fun serialize(value: BuildSystemCoordinates): String = "${value.groupId}:${value.artifactId}:${value.version}" From 8a1efc24dad8e3a8f5b1b0fe3629f10d8375852f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 18 Jun 2024 17:14:20 +0200 Subject: [PATCH 096/118] Add rawVersionFilter to maven artifact version property --- .../MavenArtifactVersionCreatorProperty.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index f06a51e64..64c5cc4aa 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -21,10 +21,13 @@ package com.demonwav.mcdev.creator.custom.types import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext +import com.intellij.openapi.diagnostic.getOrLogException +import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.ui.ComboboxSpeedSearch @@ -44,6 +47,7 @@ class MavenArtifactVersionCreatorProperty( ) : SemanticVersionCreatorProperty(descriptor, graph, properties) { lateinit var sourceUrl: String + var rawVersionFilter: (String) -> Boolean = { true } override val graphProperty: GraphProperty = graph.property(SemanticVersion(emptyList())) private val versionsProperty = graph.property>(emptyList()) @@ -79,10 +83,24 @@ class MavenArtifactVersionCreatorProperty( sourceUrl = url + val rawVersionFilterCondition = descriptor.parameters?.get("rawVersionFilter") + if (rawVersionFilterCondition != null) { + if (rawVersionFilterCondition !is String) { + reporter.error("'rawVersionFilter' must be a string") + } else { + rawVersionFilter = { version -> + val props = mapOf("version" to version) + TemplateEvaluator.condition(props, rawVersionFilterCondition) + .getOrLogException(thisLogger()) == true + } + } + } + application.executeOnPooledThread { runBlocking { val versions = collectMavenVersions(sourceUrl) .asSequence() + .filter(rawVersionFilter) .mapNotNull(SemanticVersion::tryParse) .sortedDescending() .take(descriptor.limit ?: 50) From 8a755b4196f728d9ac4a72aa1edabe6c25a3b884 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 20 Jun 2024 05:50:25 +0200 Subject: [PATCH 097/118] Add versionFilter to maven artifact version property --- .../types/MavenArtifactVersionCreatorProperty.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index 64c5cc4aa..671f1cc05 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -48,6 +48,7 @@ class MavenArtifactVersionCreatorProperty( lateinit var sourceUrl: String var rawVersionFilter: (String) -> Boolean = { true } + var versionFilter: (SemanticVersion) -> Boolean = { true } override val graphProperty: GraphProperty = graph.property(SemanticVersion(emptyList())) private val versionsProperty = graph.property>(emptyList()) @@ -96,12 +97,26 @@ class MavenArtifactVersionCreatorProperty( } } + val versionFilterCondition = descriptor.parameters?.get("versionFilter") + if (versionFilterCondition != null) { + if (versionFilterCondition !is String) { + reporter.error("'versionFilter' must be a string") + } else { + versionFilter = { version -> + val props = mapOf("version" to version) + TemplateEvaluator.condition(props, versionFilterCondition) + .getOrLogException(thisLogger()) == true + } + } + } + application.executeOnPooledThread { runBlocking { val versions = collectMavenVersions(sourceUrl) .asSequence() .filter(rawVersionFilter) .mapNotNull(SemanticVersion::tryParse) + .filter(versionFilter) .sortedDescending() .take(descriptor.limit ?: 50) .toList() From 8080e21a7dd2792c5298e23ab31779f88af9f7fb Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 22 Jun 2024 00:15:49 +0200 Subject: [PATCH 098/118] Fix dropdown default values and add validation to ensure selection is valid --- .../creator/custom/BuiltinValidations.kt | 15 ++++++++++- .../creator/custom/TemplateDescriptor.kt | 2 +- .../custom/types/SimpleCreatorProperty.kt | 26 ++++++++++++++++--- .../messages/MinecraftDevelopment.properties | 2 ++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/creator/custom/BuiltinValidations.kt b/src/main/kotlin/creator/custom/BuiltinValidations.kt index b57d6610b..8fd1a8401 100644 --- a/src/main/kotlin/creator/custom/BuiltinValidations.kt +++ b/src/main/kotlin/creator/custom/BuiltinValidations.kt @@ -28,6 +28,7 @@ import com.intellij.openapi.ui.ValidationInfo import com.intellij.openapi.ui.validation.DialogValidation import com.intellij.openapi.ui.validation.validationErrorIf import com.intellij.openapi.util.text.StringUtil +import javax.swing.JComponent object BuiltinValidations { val nonBlank = validationErrorIf(MCDevBundle("creator.validation.blank")) { it.isBlank() } @@ -61,5 +62,17 @@ object BuiltinValidations { } fun byRegex(regex: Regex): DialogValidation.WithParameter<() -> String> = - validationErrorIf("Must match regex $regex") { !it.matches(regex) } + validationErrorIf(MCDevBundle("creator.validation.regex", regex)) { !it.matches(regex) } + + fun isAnyOf( + selectionGetter: () -> T, + options: Collection, + component: JComponent? = null + ): DialogValidation = DialogValidation { + if (selectionGetter() !in options) { + return@DialogValidation ValidationInfo(MCDevBundle("creator.validation.invalid_option"), component) + } + + return@DialogValidation null + } } diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index aa715828c..6339b9de3 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -54,7 +54,7 @@ data class TemplatePropertyDescriptor( val editable: Boolean? = null, val collapsible: Boolean? = null, val warning: String? = null, - val default: Any, + val default: Any?, val nullIfDefault: Boolean? = null, val derives: PropertyDerivation? = null, val inheritFrom: String? = null, diff --git a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt index 0e814d28f..7a735d7fb 100644 --- a/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SimpleCreatorProperty.kt @@ -20,6 +20,8 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty @@ -67,7 +69,7 @@ abstract class SimpleCreatorProperty( if (descriptor.default is Number && descriptor.options is List<*>) { descriptor.options[descriptor.default.toInt()] } else { - options!![createDefaultValue(descriptor.default)] + descriptor.default ?: options?.keys?.firstOrNull() } } else { descriptor.default @@ -80,17 +82,33 @@ abstract class SimpleCreatorProperty( override fun buildUi(panel: Panel, context: WizardContext) { if (isDropdown) { + if (graphProperty.get() !in options!!.keys) { + graphProperty.set(defaultValue) + } + panel.row(descriptor.translatedLabel) { if (descriptor.forceDropdown == true) { - comboBox(options!!.keys, DropdownAutoRenderer()) + comboBox(options.keys, DropdownAutoRenderer()) .bindItem(graphProperty) .enabled(descriptor.editable != false) - .also { ComboboxSpeedSearch.installOn(it.component) } + .also { + val component = it.component + ComboboxSpeedSearch.installOn(component) + val validation = + BuiltinValidations.isAnyOf(component::getSelectedItem, options.keys, component) + it.validationOnInput(validation) + it.validationOnApply(validation) + } } else { - segmentedButton(options!!.keys) { options[it] ?: it.toString() } + segmentedButton(options.keys) { options[it] ?: it.toString() } .bind(graphProperty) .enabled(descriptor.editable != false) .maxButtonsCount(4) + .validation { + val message = MCDevBundle("creator.validation.invalid_option") + addInputRule(message) { it.selectedItem !in options.keys } + addApplyRule(message) { it.selectedItem !in options.keys } + } } }.propertyVisibility() } else { diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 1211f3678..c1e2268bf 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -131,6 +131,8 @@ creator.validation.blank=Must not be blank creator.validation.group_id_non_example=Group ID must be changed from "org.example" creator.validation.semantic_version=Version must be a valid semantic version creator.validation.class_fqn=Must be a valid class fully qualified name +creator.validation.regex=Must match regex {0} +creator.validation.invalid_option=Selection is not a valid option creator.validation.jdk_preferred=Java {0} is recommended for {1} creator.validation.jdk_preferred_default_reason=these settings From fa6b94430d31233ec89e72029c6122478202bfca Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 24 Jun 2024 13:25:43 +0200 Subject: [PATCH 099/118] Add Bungeecord and Spigot Kotlin templates Also fix IntegerCreatorProperty default value --- src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt index f82ebc623..bcd6edc6b 100644 --- a/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/IntegerCreatorProperty.kt @@ -39,7 +39,7 @@ class IntegerCreatorProperty( properties: Map> ) : SimpleCreatorProperty(descriptor, graph, properties, Int::class.java) { - override fun createDefaultValue(raw: Any?): Int = raw as? Int ?: 0 + override fun createDefaultValue(raw: Any?): Int = (raw as? Number)?.toInt() ?: 0 override fun serialize(value: Int): String = value.toString() From f8a0dd354856e18d26bf09a376ce3777ab67e87f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 25 Jun 2024 15:18:32 +0200 Subject: [PATCH 100/118] Fix Parchment property not matching first release of a major mc version --- .../custom/types/ParchmentCreatorProperty.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt index 6bb379ab1..28826f429 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt @@ -239,13 +239,20 @@ class ParchmentCreatorProperty( val platformMcVersionPropertyName = descriptor.parameters?.get("minecraftVersionProperty") as? String val platformMcVersionProperty = properties[platformMcVersionPropertyName] - return when (val version = platformMcVersionProperty?.get()) { + val version = when (val version = platformMcVersionProperty?.get()) { is SemanticVersion -> version is HasMinecraftVersion -> version.minecraftVersion - else -> null + else -> return null + } + + // Ensures we get no trailing .0 for the first major version (1.21.0 -> 1.21) + // This is required because otherwise those versions won't be properly compared against Parchment's + val normalizedVersion = version.parts.dropLastWhile { part -> + part is SemanticVersion.Companion.VersionPart.ReleasePart && part.version == 0 } - } + return SemanticVersion(normalizedVersion) + } class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, From 84375b6a80fcc2888232e09ca95701d5542904c0 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 29 Jun 2024 17:53:45 +0200 Subject: [PATCH 101/118] NeoForge Kotlin templates --- .../creator/custom/types/CreatorProperty.kt | 12 +++ .../types/ForgeVersionsCreatorProperty.kt | 74 ++++++++++++------- .../types/SemanticVersionCreatorProperty.kt | 9 +++ src/main/kotlin/util/MinecraftVersions.kt | 1 + 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 77188ac7c..0de0db84b 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -181,6 +181,18 @@ abstract class CreatorProperty( return "$base.$discriminator" } + protected fun collectPropertiesValues(names: List? = null): MutableMap { + val into = mutableMapOf() + + into.putAll(TemplateEvaluator.baseProperties) + + return if (names == null) { + properties.mapValuesTo(into) { (_, prop) -> prop.get() } + } else { + names.associateWithTo(mutableMapOf()) { properties[it]?.get() } + } + } + protected fun collectDerivationParents(reporter: TemplateValidationReporter? = null): List?>? = descriptor.derives?.parents?.map { parentName -> val property = properties[parentName] diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt index 1193feea0..fee96431d 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt @@ -65,6 +65,8 @@ class ForgeVersionsCreatorProperty( private val forgeVersionProperty = graphProperty.transform({ it.forge }, { versions.copy(forge = it) }) private val forgeVersionsModel = DefaultComboBoxModel() + private var mcVersionFilterParents: List? = null + override fun createDefaultValue(raw: Any?): ForgeVersions { if (raw is String) { return deserialize(raw) @@ -121,38 +123,60 @@ class ForgeVersionsCreatorProperty( forgeVersionProperty.set(availableForgeVersions.firstOrNull() ?: emptyVersion) } - application.executeOnPooledThread { - runBlocking { - val forgeVersions = ForgeVersion.downloadData() - val mcVersions = forgeVersions?.sortedMcVersions?.let { mcVersion -> - val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String - if (filterExpr != null) { - mcVersion.filter { version -> - val conditionProps = mapOf("MC_VERSION" to version) - TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true) - } - } else { - mcVersion + descriptor.parameters?.get("mcVersionFilterParents")?.let { parents -> + if (parents !is List<*> || parents.any { it !is String }) { + reporter.error("mcVersionFilterParents must be a list of strings") + } else { + @Suppress("UNCHECKED_CAST") + this.mcVersionFilterParents = parents as List + for (parent in parents) { + val parentProp = properties[parent] + if (parentProp == null) { + reporter.error("Unknown mcVersionFilter parent $parent") + continue + } + + parentProp.graphProperty.afterChange { + reloadMinecraftVersions() } } + } + } - if (forgeVersions != null && !mcVersions.isNullOrEmpty()) { - withContext(Dispatchers.Swing) { - forgeVersion = forgeVersions + application.executeOnPooledThread { + runBlocking { + forgeVersion = ForgeVersion.downloadData() + withContext(Dispatchers.Swing) { + reloadMinecraftVersions() + } + } + } + } - mcVersionsModel.removeAllElements() - mcVersionsModel.addAll(mcVersions) + private fun reloadMinecraftVersions() { + val forgeVersions = forgeVersion + ?: return - val selectedMcVersion = when { - mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() - defaultValue.minecraft in mcVersions -> defaultValue.minecraft - else -> mcVersions.first() - } - mcVersionProperty.set(selectedMcVersion) - } - } + val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String + val mcVersions = if (filterExpr != null) { + val conditionProps = collectPropertiesValues(mcVersionFilterParents) + forgeVersions.sortedMcVersions.filter { version -> + conditionProps["MC_VERSION"] = version + TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true) } + } else { + forgeVersions.sortedMcVersions + } + + mcVersionsModel.removeAllElements() + mcVersionsModel.addAll(mcVersions) + + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraft in mcVersions -> defaultValue.minecraft + else -> mcVersions.first() } + mcVersionProperty.set(selectedMcVersion) } class Factory : CreatorPropertyFactory { diff --git a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt index f45b2a467..f500d03d0 100644 --- a/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/SemanticVersionCreatorProperty.kt @@ -25,6 +25,7 @@ import com.demonwav.mcdev.creator.custom.TemplatePropertyDescriptor import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.derivation.ExtractVersionMajorMinorPropertyDerivation import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation +import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.PropertyGraph @@ -64,9 +65,17 @@ open class SemanticVersionCreatorProperty( ExtractVersionMajorMinorPropertyDerivation.create(reporter, parents, derives) } + null -> { + SelectPropertyDerivation.create(reporter, emptyList(), derives) + } + else -> null } + override fun convertSelectDerivationResult(original: Any?): Any? { + return (original as? String)?.let(SemanticVersion::tryParse) + } + class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, diff --git a/src/main/kotlin/util/MinecraftVersions.kt b/src/main/kotlin/util/MinecraftVersions.kt index 939571683..7333c5aa3 100644 --- a/src/main/kotlin/util/MinecraftVersions.kt +++ b/src/main/kotlin/util/MinecraftVersions.kt @@ -36,6 +36,7 @@ object MinecraftVersions { val MC1_19_3 = SemanticVersion.release(1, 19, 3) val MC1_19_4 = SemanticVersion.release(1, 19, 4) val MC1_20 = SemanticVersion.release(1, 20) + val MC1_20_1 = SemanticVersion.release(1, 20, 1) val MC1_20_2 = SemanticVersion.release(1, 20, 2) val MC1_20_3 = SemanticVersion.release(1, 20, 3) val MC1_20_4 = SemanticVersion.release(1, 20, 4) From dcc5ca207feb4e2ec88225e5de57585abddf823e Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sat, 29 Jun 2024 18:12:05 +0200 Subject: [PATCH 102/118] Add $version placeholder to remote url --- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 5 +++++ .../creator/custom/providers/BuiltinTemplateProvider.kt | 2 +- .../creator/custom/providers/RemoteTemplateProvider.kt | 6 +++++- .../kotlin/creator/custom/providers/TemplateProvider.kt | 2 +- src/main/resources/messages/MinecraftDevelopment.properties | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index 6339b9de3..d54dbd524 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -37,6 +37,11 @@ data class TemplateDescriptor( val translatedGroup: String get() = translate("creator.ui.group.${(group ?: "default").lowercase()}.label") + + companion object { + + const val FORMAT_VERSION = 1 + } } data class TemplatePropertyDescriptor( diff --git a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt index 7c13543e4..f2aada7d5 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt @@ -35,7 +35,7 @@ import javax.swing.JComponent class BuiltinTemplateProvider : RemoteTemplateProvider() { - private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/main.zip" + private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/\$version.zip" private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") private var repoUpdated: Boolean = false diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index 53246d177..5279a2b7e 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -23,6 +23,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.creator.custom.BuiltinValidations +import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil @@ -81,11 +82,13 @@ open class RemoteTemplateProvider : TemplateProvider { protected fun doUpdateRepo( indicator: ProgressIndicator, repoName: String, - repoUrl: String, + originalRepoUrl: String, destination: Path ): Boolean { indicator.text2 = "Updating remote repository $repoName" + val repoUrl = originalRepoUrl.replace("\$version", TemplateDescriptor.FORMAT_VERSION.toString()) + val manager = FuelManager() manager.proxy = selectProxy(repoUrl) val (_, _, result) = manager.get(repoUrl) @@ -156,6 +159,7 @@ open class RemoteTemplateProvider : TemplateProvider { return panel { row(MCDevBundle("creator.ui.custom.remote.url.label")) { textField() + .comment(MCDevBundle("creator.ui.custom.remote.url.comment")) .align(AlignX.FILL) .columns(COLUMNS_LARGE) .bindText(urlProperty) diff --git a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt index 79b0a207b..30efddbb2 100644 --- a/src/main/kotlin/creator/custom/providers/TemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/TemplateProvider.kt @@ -167,7 +167,7 @@ interface TemplateProvider { ): VfsLoadedTemplate? { descriptorFile.refreshSync(modalityState) var descriptor = Gson().fromJson(descriptorFile.readText()) - if (descriptor.version != 1) { + if (descriptor.version != TemplateDescriptor.FORMAT_VERSION) { thisLogger().warn("Cannot handle template ${descriptorFile.path} of version ${descriptor.version}") return null } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index c1e2268bf..391b35e0b 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -46,6 +46,7 @@ creator.ui.custom.path.dialog.description=Select the root directory of the templ creator.ui.custom.archive.dialog.title=Template Archive creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template creator.ui.custom.remote.url.label=Download URL: +creator.ui.custom.remote.url.comment='$version' will be replaced by the template descriptor version currently in use creator.ui.custom.remote.auto_update.label=Auto update creator.ui.warn.no_properties=This template has no properties From 174fec9ef1f925d3ef99d54aced66e48ec62f9ea Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 30 Jun 2024 00:27:02 +0200 Subject: [PATCH 103/118] Fixup github archive matcher --- .../kotlin/creator/custom/providers/BuiltinTemplateProvider.kt | 2 +- .../kotlin/creator/custom/providers/RemoteTemplateProvider.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt index f2aada7d5..e33f86b83 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt @@ -35,7 +35,7 @@ import javax.swing.JComponent class BuiltinTemplateProvider : RemoteTemplateProvider() { - private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/\$version.zip" + private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/v\$version.zip" private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") private var repoUpdated: Boolean = false diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index 5279a2b7e..6119df454 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -113,7 +113,7 @@ open class RemoteTemplateProvider : TemplateProvider { // In such cases there is a single directory in the root directory of the zip // We simply move all its children to the base directory so the rest of the system uses the correct // root directory for this repository - val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/(.*?).zip".toRegex() + val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/[vV]?(.*?).zip".toRegex() val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(repoUrl) if (githubRepoArchiveMatcher != null) { val githubRepoName = githubRepoArchiveMatcher.groupValues[2] From 3c3b5babed830248d9dacad4996a80b21512b205 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 30 Jun 2024 02:03:39 +0200 Subject: [PATCH 104/118] Add Fabric split sources --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 8 +++++++- src/main/kotlin/creator/custom/TemplateDescriptor.kt | 1 + .../resources/messages/MinecraftDevelopment.properties | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 4c69223cc..8ca8bbe12 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -469,7 +469,13 @@ class CustomPlatformStep( continue } - val processedContent = TemplateEvaluator.template(templateProperties, templateContents) + var fileTemplateProperties = templateProperties + if (file.properties != null) { + fileTemplateProperties = templateProperties.toMutableMap() + fileTemplateProperties.putAll(file.properties) + } + + val processedContent = TemplateEvaluator.template(fileTemplateProperties, templateContents) .getOrLogException(thisLogger()) ?: continue diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index d54dbd524..d8aafc13d 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -93,6 +93,7 @@ data class TemplateFile( val template: String, val destination: String, val condition: String? = null, + val properties: Map? = null, val reformat: Boolean? = null, val openInEditor: Boolean? = null, ) diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 391b35e0b..4243c0037 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -74,6 +74,7 @@ creator.ui.load_at.option.startup=Startup: creator.ui.load_at.option.postworld=Post World: creator.ui.soft_depend.label=Soft Depend: creator.ui.use_mixins.label=Use &Mixins: +creator.ui.split_sources.label=Split Sources: creator.ui.java_version.label=Java Version: creator.ui.jdk.label=JDK: creator.ui.optional_settings.label=Optional Settings From ae4be36b1af8c8d19555a08a65fe441760b016db Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 30 Jun 2024 11:48:27 +0200 Subject: [PATCH 105/118] Use Neo's ModDev plugin in 1.21 --- .../creator/custom/model/NeoForgeVersions.kt | 1 + .../types/NeoForgeVersionsCreatorProperty.kt | 28 +++++------ .../neoforge/version/NeoModDevVersion.kt | 50 +++++++++++++++++++ .../messages/MinecraftDevelopment.properties | 1 - 4 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/platform/neoforge/version/platform/neoforge/version/NeoModDevVersion.kt diff --git a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt index add389b79..c5a9bb2a2 100644 --- a/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt +++ b/src/main/kotlin/creator/custom/model/NeoForgeVersions.kt @@ -27,6 +27,7 @@ data class NeoForgeVersions( val minecraft: SemanticVersion, val neoforge: SemanticVersion, val neogradle: SemanticVersion, + val moddev: SemanticVersion, ) : HasMinecraftVersion { override val minecraftVersion = minecraft diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index 760970fb2..715efdfbe 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -28,6 +28,7 @@ import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.model.NeoForgeVersions import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion import com.demonwav.mcdev.platform.neoforge.version.NeoGradleVersion +import com.demonwav.mcdev.platform.neoforge.version.platform.neoforge.version.NeoModDevVersion import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty @@ -65,29 +66,30 @@ class NeoForgeVersionsCreatorProperty( private val nfVersionProperty = graphProperty.transform({ it.neoforge }, { versions.copy(neoforge = it) }) private val nfVersionsModel = DefaultComboBoxModel() private val ngVersionProperty = graphProperty.transform({ it.neogradle }, { versions.copy(neogradle = it) }) - private val ngVersionsModel = DefaultComboBoxModel() + private val mdVersionProperty = graphProperty.transform({ it.moddev }, { versions.copy(moddev = it) }) override fun createDefaultValue(raw: Any?): NeoForgeVersions { if (raw is String) { return deserialize(raw) } - return NeoForgeVersions(emptyVersion, emptyVersion, emptyVersion) + return NeoForgeVersions(emptyVersion, emptyVersion, emptyVersion, emptyVersion) } override fun serialize(value: NeoForgeVersions): String { - return "${value.minecraft} ${value.neoforge} ${value.neogradle}" + return "${value.minecraft} ${value.neoforge} ${value.neogradle} ${value.moddev}" } override fun deserialize(string: String): NeoForgeVersions { val versions = string.split(' ') - .take(3) + .take(4) .map { SemanticVersion.tryParse(it) ?: emptyVersion } return NeoForgeVersions( versions.getOrNull(0) ?: emptyVersion, versions.getOrNull(1) ?: emptyVersion, versions.getOrNull(2) ?: emptyVersion, + versions.getOrNull(3) ?: emptyVersion, ) } @@ -105,13 +107,6 @@ class NeoForgeVersionsCreatorProperty( .validationOnInput(BuiltinValidations.nonEmptyVersion) .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - - label(MCDevBundle("creator.ui.neogradle_version.label")).gap(RightGap.SMALL) - comboBox(ngVersionsModel) - .bindItem(ngVersionProperty) - .validationOnInput(BuiltinValidations.nonEmptyVersion) - .validationOnApply(BuiltinValidations.nonEmptyVersion) - .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) } @@ -135,6 +130,7 @@ class NeoForgeVersionsCreatorProperty( runBlocking { val neoforgeVersions = NeoForgeVersion.downloadData() val neogradleVersions = NeoGradleVersion.downloadData() + val moddevVersions = NeoModDevVersion.downloadData() val mcVersions = neoforgeVersions?.sortedMcVersions?.let { mcVersion -> val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String if (filterExpr != null) { @@ -147,7 +143,9 @@ class NeoForgeVersionsCreatorProperty( } } - if (neoforgeVersions != null && neogradleVersions != null && !mcVersions.isNullOrEmpty()) { + if (neoforgeVersions != null && neogradleVersions != null && + moddevVersions != null && !mcVersions.isNullOrEmpty() + ) { withContext(Dispatchers.Swing) { nfVersion = neoforgeVersions @@ -161,10 +159,8 @@ class NeoForgeVersionsCreatorProperty( } mcVersionProperty.set(selectedMcVersion) - val availableNgVersions = neogradleVersions.versions.take(descriptor.limit ?: 50) - ngVersionsModel.removeAllElements() - ngVersionsModel.addAll(availableNgVersions) - ngVersionProperty.set(availableNgVersions.firstOrNull() ?: emptyVersion) + ngVersionProperty.set(neogradleVersions.versions.firstOrNull() ?: emptyVersion) + mdVersionProperty.set(moddevVersions.versions.firstOrNull() ?: emptyVersion) } } } diff --git a/src/main/kotlin/platform/neoforge/version/platform/neoforge/version/NeoModDevVersion.kt b/src/main/kotlin/platform/neoforge/version/platform/neoforge/version/NeoModDevVersion.kt new file mode 100644 index 000000000..c6d8ac4d0 --- /dev/null +++ b/src/main/kotlin/platform/neoforge/version/platform/neoforge/version/NeoModDevVersion.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.neoforge.version.platform.neoforge.version + +import com.demonwav.mcdev.creator.collectMavenVersions +import com.demonwav.mcdev.util.SemanticVersion +import com.intellij.openapi.diagnostic.logger +import java.io.IOException + +class NeoModDevVersion private constructor(val versions: List) { + + companion object { + private val LOGGER = logger() + + suspend fun downloadData(): NeoModDevVersion? { + try { + val url = "https://maven.neoforged.net/releases/net/neoforged/moddev" + + "/net.neoforged.moddev.gradle.plugin/maven-metadata.xml" + val versions = collectMavenVersions(url) + .asSequence() + .mapNotNull(SemanticVersion.Companion::tryParse) + .sortedDescending() + .take(50) + .toList() + return NeoModDevVersion(versions) + } catch (e: IOException) { + LOGGER.error("Failed to retrieve NeoForged ModDev version data", e) + } + return null + } + } +} diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 4243c0037..d879ff8d2 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -89,7 +89,6 @@ creator.ui.mod_environment.option.client=Client creator.ui.mod_environment.option.server=Server creator.ui.forge_version.label=Forge: creator.ui.neoforge_version.label=NeoForge: -creator.ui.neogradle_version.label=NeoGradle: creator.ui.show_snapshots.label=Show snapshots: creator.ui.loom_version.label=Loom Version: creator.ui.loader_version.label=Loader Version: From fad2ebb8dc67714052f10001ccd1ce30c2a44bdf Mon Sep 17 00:00:00 2001 From: RedNesto Date: Sun, 30 Jun 2024 11:48:42 +0200 Subject: [PATCH 106/118] Improve template error reporting --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index 8ca8bbe12..a25fe1fbb 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -38,6 +38,7 @@ import com.intellij.ide.wizard.AbstractNewProjectWizardStep import com.intellij.ide.wizard.GitNewProjectWizardData import com.intellij.ide.wizard.NewProjectWizardBaseData import com.intellij.ide.wizard.NewProjectWizardStep +import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.logger @@ -476,7 +477,11 @@ class CustomPlatformStep( } val processedContent = TemplateEvaluator.template(fileTemplateProperties, templateContents) - .getOrLogException(thisLogger()) + .onFailure { t -> + val attachment = Attachment(relativeTemplate, templateContents) + thisLogger().error("Failed evaluate template '$relativeTemplate'", t, attachment) + } + .getOrNull() ?: continue destPath.parent.createDirectories() From dc58e6bf89230d4f1bd1f469fefb27ee6ebda7c5 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 2 Jul 2024 16:14:21 +0200 Subject: [PATCH 107/118] Fix Loom's default version selection --- .../creator/custom/types/FabricVersionsCreatorProperty.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt index 51ba259ee..4839292d7 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt @@ -241,9 +241,9 @@ class FabricVersionsCreatorProperty( if (loomVersions != null) { loomVersionModel.removeAllElements() loomVersionModel.addAll(loomVersions) - val defaultValue = loomVersions.firstOrNull { - it.parts.none { it is SemanticVersion.Companion.VersionPart.PreReleasePart } - } ?: loomVersions.firstOrNull() ?: emptyVersion + val defaultValue = loomVersions.firstOrNull { it.toString().endsWith("-SNAPSHOT") } + ?: loomVersions.firstOrNull() + ?: emptyVersion loomVersionProperty.set(defaultValue) } From 1c64ab4153e39bde4177b4aef424919b1cf8b720 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Tue, 2 Jul 2024 17:05:00 +0200 Subject: [PATCH 108/118] No longer unzip remote templates Instead read directly inside them, and allow to configure a different repo root in cases like GitHub's zips, that have a root directory named after the repo and branch name --- .../providers/BuiltinTemplateProvider.kt | 9 +- .../providers/RemoteTemplateProvider.kt | 117 +++++++++++------- .../messages/MinecraftDevelopment.properties | 2 + 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt index e33f86b83..1bc3bc9b6 100644 --- a/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/BuiltinTemplateProvider.kt @@ -22,6 +22,7 @@ package com.demonwav.mcdev.creator.custom.providers import com.demonwav.mcdev.MinecraftSettings import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.creator.custom.TemplateDescriptor import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.refreshSync @@ -37,6 +38,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() { private val builtinRepoUrl = "https://github.com/minecraft-dev/templates/archive/refs/heads/v\$version.zip" private val builtinTemplatesPath = PluginUtil.plugin.pluginPath.resolve("lib/resources/builtin-templates") + private val builtinTemplatesInnerPath = "templates-${TemplateDescriptor.FORMAT_VERSION}" private var repoUpdated: Boolean = false override val label: String = MCDevBundle("template.provider.builtin.label") @@ -49,7 +51,7 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() { return } - if (doUpdateRepo(indicator, label, builtinRepoUrl, builtinTemplatesPath)) { + if (doUpdateRepo(indicator, label, builtinRepoUrl)) { repoUpdated = true } } @@ -58,6 +60,11 @@ class BuiltinTemplateProvider : RemoteTemplateProvider() { context: WizardContext, repo: MinecraftSettings.TemplateRepo ): Collection { + val remoteTemplates = doLoadTemplates(context, repo, builtinTemplatesInnerPath) + if (remoteTemplates.isNotEmpty()) { + return remoteTemplates + } + val repoRoot = builtinTemplatesPath.virtualFile ?: return emptyList() repoRoot.refreshSync(context.modalityState) diff --git a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt index 6119df454..2cbc70f16 100644 --- a/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt +++ b/src/main/kotlin/creator/custom/providers/RemoteTemplateProvider.kt @@ -28,7 +28,6 @@ import com.demonwav.mcdev.creator.modalityState import com.demonwav.mcdev.creator.selectProxy import com.demonwav.mcdev.update.PluginUtil import com.demonwav.mcdev.util.refreshSync -import com.demonwav.mcdev.util.virtualFile import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.result.getOrNull import com.github.kittinunf.result.onError @@ -40,7 +39,7 @@ import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.trim import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.JarFileSystem import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.COLUMNS_LARGE import com.intellij.ui.dsl.builder.bindSelected @@ -48,12 +47,11 @@ import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.columns import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.textValidation -import com.intellij.util.io.ZipUtil import com.intellij.util.io.createDirectories import java.nio.file.Path import javax.swing.JComponent -import kotlin.io.path.listDirectoryEntries -import kotlin.io.path.moveTo +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists import kotlin.io.path.writeBytes open class RemoteTemplateProvider : TemplateProvider { @@ -73,7 +71,7 @@ open class RemoteTemplateProvider : TemplateProvider { continue } - if (doUpdateRepo(indicator, repo.name, remote.url, remote.getDestination(repo.name))) { + if (doUpdateRepo(indicator, repo.name, remote.url)) { updatedTemplates.add(remote.url) } } @@ -82,12 +80,11 @@ open class RemoteTemplateProvider : TemplateProvider { protected fun doUpdateRepo( indicator: ProgressIndicator, repoName: String, - originalRepoUrl: String, - destination: Path + originalRepoUrl: String ): Boolean { indicator.text2 = "Updating remote repository $repoName" - val repoUrl = originalRepoUrl.replace("\$version", TemplateDescriptor.FORMAT_VERSION.toString()) + val repoUrl = replaceVariables(originalRepoUrl) val manager = FuelManager() manager.proxy = selectProxy(repoUrl) @@ -102,26 +99,9 @@ open class RemoteTemplateProvider : TemplateProvider { }.getOrNull() ?: return false try { - val remoteTemplatesDir = destination - remoteTemplatesDir.createDirectories() - val zipPath = remoteTemplatesDir.resolveSibling("$repoName.zip") + val zipPath = RemoteTemplateRepo.getDestinationZip(repoName) + zipPath.parent.createDirectories() zipPath.writeBytes(data) - FileUtil.deleteRecursively(remoteTemplatesDir) - ZipUtil.extract(zipPath, remoteTemplatesDir, null) - - // Loose way to find out if the url is a github repo archive - // In such cases there is a single directory in the root directory of the zip - // We simply move all its children to the base directory so the rest of the system uses the correct - // root directory for this repository - val githubRepoArchiveRegex = "https://github\\.com/(.*?)/(.*?)/archive/refs/heads/[vV]?(.*?).zip".toRegex() - val githubRepoArchiveMatcher = githubRepoArchiveRegex.matchEntire(repoUrl) - if (githubRepoArchiveMatcher != null) { - val githubRepoName = githubRepoArchiveMatcher.groupValues[2] - val branchName = githubRepoArchiveMatcher.groupValues[3] - for (child in remoteTemplatesDir.resolve("$githubRepoName-$branchName").listDirectoryEntries()) { - child.moveTo(remoteTemplatesDir.resolve(child.fileName)) - } - } thisLogger().info("Remote templates repository update applied successfully") return true @@ -138,15 +118,46 @@ open class RemoteTemplateProvider : TemplateProvider { context: WizardContext, repo: MinecraftSettings.TemplateRepo ): Collection { - val remote = RemoteTemplateRepo.deserialize(repo.data) + val remoteRepo = RemoteTemplateRepo.deserialize(repo.data) ?: return emptyList() - val repoRoot = remote.getDestination(repo.name).virtualFile + return doLoadTemplates(context, repo, remoteRepo.innerPath) + } + + protected fun doLoadTemplates( + context: WizardContext, + repo: MinecraftSettings.TemplateRepo, + rawInnerPath: String + ): List { + val remoteRootPath = RemoteTemplateRepo.getDestinationZip(repo.name) + if (!remoteRootPath.exists()) { + return emptyList() + } + + val archiveRoot = remoteRootPath.absolutePathString() + JarFileSystem.JAR_SEPARATOR + + val fs = JarFileSystem.getInstance() + val rootFile = fs.refreshAndFindFileByPath(archiveRoot) ?: return emptyList() val modalityState = context.modalityState - repoRoot.refreshSync(modalityState) + rootFile.refreshSync(modalityState) + + val innerPath = replaceVariables(rawInnerPath) + val repoRoot = if (innerPath.isNotBlank()) { + rootFile.findFileByRelativePath(innerPath) + } else { + rootFile + } + + if (repoRoot == null) { + return emptyList() + } + return TemplateProvider.findTemplates(modalityState, repoRoot) } + private fun replaceVariables(originalRepoUrl: String): String = + originalRepoUrl.replace("\$version", TemplateDescriptor.FORMAT_VERSION.toString()) + override fun setupConfigUi( data: String, dataSetter: (String) -> Unit @@ -155,6 +166,7 @@ open class RemoteTemplateProvider : TemplateProvider { val defaultRepo = RemoteTemplateRepo.deserialize(data) val urlProperty = propertyGraph.property(defaultRepo?.url ?: "").trim() val autoUpdateProperty = propertyGraph.property(defaultRepo?.autoUpdate != false) + val innerPathProperty = propertyGraph.property(defaultRepo?.innerPath ?: "").trim() return panel { row(MCDevBundle("creator.ui.custom.remote.url.label")) { @@ -166,37 +178,50 @@ open class RemoteTemplateProvider : TemplateProvider { .textValidation(BuiltinValidations.nonBlank) } + row(MCDevBundle("creator.ui.custom.remote.inner_path.label")) { + textField() + .comment(MCDevBundle("creator.ui.custom.remote.inner_path.comment")) + .align(AlignX.FILL) + .columns(COLUMNS_LARGE) + .bindText(innerPathProperty) + } + row { checkBox(MCDevBundle("creator.ui.custom.remote.auto_update.label")) .bindSelected(autoUpdateProperty) } onApply { - val repo = RemoteTemplateRepo(urlProperty.get(), autoUpdateProperty.get()) + val repo = RemoteTemplateRepo(urlProperty.get(), autoUpdateProperty.get(), innerPathProperty.get()) dataSetter(repo.serialize()) } } } - data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean) { - - fun getDestination(repoName: String): Path { - return PathManager.getSystemDir().resolve("mcdev-templates").resolve(repoName) - } + data class RemoteTemplateRepo(val url: String, val autoUpdate: Boolean, val innerPath: String) { - fun serialize(): String = "$url\n$autoUpdate" + fun serialize(): String = "$url\n$autoUpdate\n$innerPath" companion object { + + val templatesBaseDir: Path + get() = PathManager.getSystemDir().resolve("mcdev-templates") + + fun getDestinationZip(repoName: String): Path { + return templatesBaseDir.resolve("$repoName.zip") + } + fun deserialize(data: String): RemoteTemplateRepo? { - val lines = data.lines() - return when (lines.size) { - 0 -> null - 1 -> RemoteTemplateRepo(lines[0], true) - else -> { - val (url, autoUpdate) = lines - RemoteTemplateRepo(url, autoUpdate.toBoolean()) - } + if (data.isBlank()) { + return null } + + val lines = data.lines() + return RemoteTemplateRepo( + lines[0], + lines.getOrNull(1).toBoolean(), + lines.getOrNull(2) ?: "", + ) } } } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index d879ff8d2..5753b9c93 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -47,6 +47,8 @@ creator.ui.custom.archive.dialog.title=Template Archive creator.ui.custom.archive.dialog.description=Select the ZIP file containing the template creator.ui.custom.remote.url.label=Download URL: creator.ui.custom.remote.url.comment='$version' will be replaced by the template descriptor version currently in use +creator.ui.custom.remote.inner_path.label=Inner Path: +creator.ui.custom.remote.inner_path.comment='$version' will be replaced by the template descriptor version currently in use creator.ui.custom.remote.auto_update.label=Auto update creator.ui.warn.no_properties=This template has no properties From 4a1d63cce4a9d6b17bfc3d9204d1ec172a91c35c Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 8 Jul 2024 17:54:35 +0200 Subject: [PATCH 109/118] Cache downloaded versions --- .../ArchitecturyVersionsCreatorProperty.kt | 111 ++++++++++-------- .../types/FabricVersionsCreatorProperty.kt | 92 +++++++++------ .../types/ForgeVersionsCreatorProperty.kt | 35 ++++-- .../MavenArtifactVersionCreatorProperty.kt | 64 ++++++++-- .../types/NeoForgeVersionsCreatorProperty.kt | 86 +++++++++----- .../custom/types/ParchmentCreatorProperty.kt | 48 +++++--- 6 files changed, 284 insertions(+), 152 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt index a623aa655..dc73df32c 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt @@ -46,6 +46,7 @@ import com.intellij.ui.dsl.builder.bindSelected import com.intellij.util.application import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext @@ -72,13 +73,6 @@ class ArchitecturyVersionsCreatorProperty( ) private val defaultValue = createDefaultValue(descriptor.default) - private var forgeVersions: ForgeVersion? = null - private var neoForgeVersions: NeoForgeVersion? = null - private var fabricVersions: FabricVersions? = null - private var loomVersions: List? = null - private var fabricApiVersions: FabricApiVersions? = null - private var architecturyVersions: ArchitecturyVersion? = null - override val graphProperty: GraphProperty = graph.property(defaultValue) var model: ArchitecturyVersionsModel by graphProperty @@ -274,50 +268,26 @@ class ArchitecturyVersionsCreatorProperty( updateArchitecturyApiVersions() } - application.executeOnPooledThread { - runBlocking { - val forgeVersionsJob = asyncIO { ForgeVersion.downloadData() } - val neoForgeVersionsJob = asyncIO { NeoForgeVersion.downloadData() } - val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } - val loomVersionsJob = asyncIO { - collectMavenVersions( - "https://maven.architectury.dev/dev/architectury/architectury-loom/maven-metadata.xml" - ) - } - val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } - val architecturyVersionsJob = asyncIO { ArchitecturyVersion.downloadData() } - - this@ArchitecturyVersionsCreatorProperty.forgeVersions = forgeVersionsJob.await() - this@ArchitecturyVersionsCreatorProperty.neoForgeVersions = neoForgeVersionsJob.await() - this@ArchitecturyVersionsCreatorProperty.fabricVersions = fabricVersionsJob.await() - this@ArchitecturyVersionsCreatorProperty.loomVersions = loomVersionsJob.await() - .mapNotNull(SemanticVersion::tryParse) - .sortedDescending() - this@ArchitecturyVersionsCreatorProperty.fabricApiVersions = fabricApiVersionsJob.await() - this@ArchitecturyVersionsCreatorProperty.architecturyVersions = architecturyVersionsJob.await() - - withContext(Dispatchers.Swing) { - val fabricVersions = fabricVersions - if (fabricVersions != null) { - loaderVersionModel.removeAllElements() - loaderVersionModel.addAll(fabricVersions.loader) - loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) - } - - val loomVersions = loomVersions - if (loomVersions != null) { - loomVersionModel.removeAllElements() - loomVersionModel.addAll(loomVersions) - val defaultValue = loomVersions.find { - it.parts.any { it is SemanticVersion.Companion.VersionPart.PreReleasePart } - } ?: loomVersions.firstOrNull() ?: emptyVersion + downloadVersions { + val fabricVersions = fabricVersions + if (fabricVersions != null) { + loaderVersionModel.removeAllElements() + loaderVersionModel.addAll(fabricVersions.loader) + loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) + } - loomVersionProperty.set(defaultValue) - } + val loomVersions = loomVersions + if (loomVersions != null) { + loomVersionModel.removeAllElements() + loomVersionModel.addAll(loomVersions) + val defaultValue = loomVersions.find { + it.parts.any { it is SemanticVersion.Companion.VersionPart.PreReleasePart } + } ?: loomVersions.firstOrNull() ?: emptyVersion - updateMcVersionsList() - } + loomVersionProperty.set(defaultValue) } + + updateMcVersionsList() } } @@ -439,6 +409,51 @@ class ArchitecturyVersionsCreatorProperty( architecturyApiVersionProperty.set(availableArchitecturyApiVersions.firstOrNull() ?: emptyVersion) } + companion object { + private var hasDownloadedVersions = false + + private var forgeVersions: ForgeVersion? = null + private var neoForgeVersions: NeoForgeVersion? = null + private var fabricVersions: FabricVersions? = null + private var loomVersions: List? = null + private var fabricApiVersions: FabricApiVersions? = null + private var architecturyVersions: ArchitecturyVersion? = null + + private fun downloadVersions(completeCallback: () -> Unit) { + if (hasDownloadedVersions) { + completeCallback() + return + } + + application.executeOnPooledThread { + runBlocking { + awaitAll( + asyncIO { ForgeVersion.downloadData().also { forgeVersions = it } }, + asyncIO { NeoForgeVersion.downloadData().also { neoForgeVersions = it } }, + asyncIO { FabricVersions.downloadData().also { fabricVersions = it } }, + asyncIO { + collectMavenVersions( + "https://maven.architectury.dev/dev/architectury/architectury-loom/maven-metadata.xml" + ).also { + loomVersions = it + .mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + } + }, + asyncIO { FabricApiVersions.downloadData().also { fabricApiVersions = it } }, + asyncIO { ArchitecturyVersion.downloadData().also { architecturyVersions = it } }, + ) + + hasDownloadedVersions = true + + withContext(Dispatchers.Swing) { + completeCallback() + } + } + } + } + } + class Factory : CreatorPropertyFactory { override fun create( diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt index 4839292d7..d2fc11222 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt @@ -45,6 +45,7 @@ import com.intellij.ui.dsl.builder.bindSelected import com.intellij.util.application import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext @@ -67,10 +68,6 @@ class FabricVersionsCreatorProperty( ) private val defaultValue = createDefaultValue(descriptor.default) - private var fabricVersions: FabricVersions? = null - private var loomVersions: List? = null - private var fabricApiVersions: FabricApiVersions? = null - override val graphProperty: GraphProperty = graph.property(defaultValue) var model: FabricVersionsModel by graphProperty @@ -213,41 +210,25 @@ class FabricVersionsCreatorProperty( updateFabricApiVersions() } - application.executeOnPooledThread { - runBlocking { - val fabricVersionsJob = asyncIO { FabricVersions.downloadData() } - val loomVersionsJob = asyncIO { - collectMavenVersions("https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml") - } - val fabricApiVersionsJob = asyncIO { FabricApiVersions.downloadData() } - - this@FabricVersionsCreatorProperty.fabricVersions = fabricVersionsJob.await() - this@FabricVersionsCreatorProperty.loomVersions = loomVersionsJob.await() - .mapNotNull(SemanticVersion::tryParse) - .sortedDescending() - this@FabricVersionsCreatorProperty.fabricApiVersions = fabricApiVersionsJob.await() - - withContext(Dispatchers.Swing) { - val fabricVersions = fabricVersions - if (fabricVersions != null) { - loaderVersionModel.removeAllElements() - loaderVersionModel.addAll(fabricVersions.loader) - loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) - - updateMcVersionsList() - } + downloadVersion { + val fabricVersions = fabricVersions + if (fabricVersions != null) { + loaderVersionModel.removeAllElements() + loaderVersionModel.addAll(fabricVersions.loader) + loaderVersionProperty.set(fabricVersions.loader.firstOrNull() ?: emptyVersion) - val loomVersions = loomVersions - if (loomVersions != null) { - loomVersionModel.removeAllElements() - loomVersionModel.addAll(loomVersions) - val defaultValue = loomVersions.firstOrNull { it.toString().endsWith("-SNAPSHOT") } - ?: loomVersions.firstOrNull() - ?: emptyVersion + updateMcVersionsList() + } - loomVersionProperty.set(defaultValue) - } - } + val loomVersions = loomVersions + if (loomVersions != null) { + loomVersionModel.removeAllElements() + loomVersionModel.addAll(loomVersions) + val defaultValue = loomVersions.firstOrNull { it.toString().endsWith("-SNAPSHOT") } + ?: loomVersions.firstOrNull() + ?: emptyVersion + + loomVersionProperty.set(defaultValue) } } } @@ -307,6 +288,43 @@ class FabricVersionsCreatorProperty( fabricApiVersionProperty.set(apiVersions.firstOrNull() ?: emptyVersion) } + companion object { + private var hasDownloadedVersions = false + + private var fabricVersions: FabricVersions? = null + private var loomVersions: List? = null + private var fabricApiVersions: FabricApiVersions? = null + + private fun downloadVersion(uiCallback: () -> Unit) { + if (hasDownloadedVersions) { + uiCallback() + return + } + + application.executeOnPooledThread { + runBlocking { + awaitAll( + asyncIO { FabricVersions.downloadData().also { fabricVersions = it } }, + asyncIO { + collectMavenVersions( + "https://maven.fabricmc.net/net/fabricmc/fabric-loom/maven-metadata.xml" + ).mapNotNull(SemanticVersion::tryParse) + .sortedDescending() + .also { loomVersions = it } + }, + asyncIO { FabricApiVersions.downloadData().also { fabricApiVersions = it } }, + ) + + hasDownloadedVersions = true + + withContext(Dispatchers.Swing) { + uiCallback() + } + } + } + } + } + class Factory : CreatorPropertyFactory { override fun create( diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt index fee96431d..a20c95da1 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt @@ -57,7 +57,6 @@ class ForgeVersionsCreatorProperty( override val graphProperty: GraphProperty = graph.property(defaultValue) var versions: ForgeVersions by graphProperty - private var forgeVersion: ForgeVersion? = null private var previousMcVersion: SemanticVersion? = null private val mcVersionProperty = graphProperty.transform({ it.minecraft }, { versions.copy(minecraft = it) }) @@ -143,13 +142,8 @@ class ForgeVersionsCreatorProperty( } } - application.executeOnPooledThread { - runBlocking { - forgeVersion = ForgeVersion.downloadData() - withContext(Dispatchers.Swing) { - reloadMinecraftVersions() - } - } + downloadVersions { + reloadMinecraftVersions() } } @@ -179,6 +173,31 @@ class ForgeVersionsCreatorProperty( mcVersionProperty.set(selectedMcVersion) } + companion object { + private var hasDownloadedVersions = false + + private var forgeVersion: ForgeVersion? = null + + private fun downloadVersions(uiCallback: () -> Unit) { + if (hasDownloadedVersions) { + uiCallback() + return + } + + application.executeOnPooledThread { + runBlocking { + forgeVersion = ForgeVersion.downloadData() + + hasDownloadedVersions = true + + withContext(Dispatchers.Swing) { + uiCallback() + } + } + } + } + } + class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, diff --git a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt index 671f1cc05..733af37ae 100644 --- a/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/MavenArtifactVersionCreatorProperty.kt @@ -35,6 +35,7 @@ import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application import com.intellij.util.ui.AsyncProcessIcon +import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing @@ -110,19 +111,56 @@ class MavenArtifactVersionCreatorProperty( } } - application.executeOnPooledThread { - runBlocking { - val versions = collectMavenVersions(sourceUrl) - .asSequence() - .filter(rawVersionFilter) - .mapNotNull(SemanticVersion::tryParse) - .filter(versionFilter) - .sortedDescending() - .take(descriptor.limit ?: 50) - .toList() - withContext(Dispatchers.Swing) { - versionsProperty.set(versions) - loadingVersionsProperty.set(false) + downloadVersions( + // The key might be a bit too unique, but that'll do the job + descriptor.name + "@" + descriptor.hashCode(), + sourceUrl, + rawVersionFilter, + versionFilter, + descriptor.limit ?: 50 + ) { versions -> + versionsProperty.set(versions) + loadingVersionsProperty.set(false) + } + } + + companion object { + + private var versionsCache = ConcurrentHashMap>() + + private fun downloadVersions( + key: String, + url: String, + rawVersionFilter: (String) -> Boolean, + versionFilter: (SemanticVersion) -> Boolean, + limit: Int, + uiCallback: (List) -> Unit + ) { + // Let's not mix up cached versions if different properties + // point to the same URL, but have different filters or limits + val cacheKey = "$key-$url" + val cachedVersions = versionsCache[cacheKey] + if (cachedVersions != null) { + uiCallback(cachedVersions) + return + } + + application.executeOnPooledThread { + runBlocking { + val versions = collectMavenVersions(url) + .asSequence() + .filter(rawVersionFilter) + .mapNotNull(SemanticVersion::tryParse) + .filter(versionFilter) + .sortedDescending() + .take(limit) + .toList() + + versionsCache[cacheKey] = versions + + withContext(Dispatchers.Swing) { + uiCallback(versions) + } } } } diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index 715efdfbe..d33fb96ca 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -30,6 +30,7 @@ import com.demonwav.mcdev.platform.neoforge.version.NeoForgeVersion import com.demonwav.mcdev.platform.neoforge.version.NeoGradleVersion import com.demonwav.mcdev.platform.neoforge.version.platform.neoforge.version.NeoModDevVersion import com.demonwav.mcdev.util.SemanticVersion +import com.demonwav.mcdev.util.asyncIO import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph @@ -41,6 +42,7 @@ import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext @@ -58,7 +60,6 @@ class NeoForgeVersionsCreatorProperty( override val graphProperty: GraphProperty = graph.property(defaultValue) var versions: NeoForgeVersions by graphProperty - private var nfVersion: NeoForgeVersion? = null private var previousMcVersion: SemanticVersion? = null private val mcVersionProperty = graphProperty.transform({ it.minecraft }, { versions.copy(minecraft = it) }) @@ -126,41 +127,64 @@ class NeoForgeVersionsCreatorProperty( nfVersionProperty.set(availableNfVersions.firstOrNull() ?: emptyVersion) } - application.executeOnPooledThread { - runBlocking { - val neoforgeVersions = NeoForgeVersion.downloadData() - val neogradleVersions = NeoGradleVersion.downloadData() - val moddevVersions = NeoModDevVersion.downloadData() - val mcVersions = neoforgeVersions?.sortedMcVersions?.let { mcVersion -> - val filterExpr = descriptor.parameters?.get("mcVersionFilter") as? String - if (filterExpr != null) { - mcVersion.filter { version -> - val conditionProps = mapOf("MC_VERSION" to version) - TemplateEvaluator.condition(conditionProps, filterExpr).getOrDefault(true) - } - } else { - mcVersion - } - } + val mcVersionFilter = descriptor.parameters?.get("mcVersionFilter") as? String + downloadVersion(mcVersionFilter) { + val mcVersions = mcVersions ?: return@downloadVersion - if (neoforgeVersions != null && neogradleVersions != null && - moddevVersions != null && !mcVersions.isNullOrEmpty() - ) { - withContext(Dispatchers.Swing) { - nfVersion = neoforgeVersions + mcVersionsModel.removeAllElements() + mcVersionsModel.addAll(mcVersions) - mcVersionsModel.removeAllElements() - mcVersionsModel.addAll(mcVersions) + val selectedMcVersion = when { + mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() + defaultValue.minecraft in mcVersions -> defaultValue.minecraft + else -> mcVersions.first() + } + mcVersionProperty.set(selectedMcVersion) + + ngVersionProperty.set(ngVersion?.versions?.firstOrNull() ?: emptyVersion) + mdVersionProperty.set(mdVersion?.versions?.firstOrNull() ?: emptyVersion) + + } + } - val selectedMcVersion = when { - mcVersionProperty.get() in mcVersions -> mcVersionProperty.get() - defaultValue.minecraft in mcVersions -> defaultValue.minecraft - else -> mcVersions.first() + companion object { + + private var hasDownloadedVersions = false + + private var nfVersion: NeoForgeVersion? = null + private var ngVersion: NeoGradleVersion? = null + private var mdVersion: NeoModDevVersion? = null + private var mcVersions: List? = null + + private fun downloadVersion(mcVersionFilter: String?, uiCallback: () -> Unit) { + if (hasDownloadedVersions) { + uiCallback() + return + } + + application.executeOnPooledThread { + runBlocking { + awaitAll( + asyncIO { NeoForgeVersion.downloadData().also { nfVersion = it } }, + asyncIO { NeoGradleVersion.downloadData().also { ngVersion = it } }, + asyncIO { NeoModDevVersion.downloadData().also { mdVersion = it } }, + ) + + mcVersions = nfVersion?.sortedMcVersions?.let { mcVersion -> + if (mcVersionFilter != null) { + mcVersion.filter { version -> + val conditionProps = mapOf("MC_VERSION" to version) + TemplateEvaluator.condition(conditionProps, mcVersionFilter).getOrDefault(true) + } + } else { + mcVersion } - mcVersionProperty.set(selectedMcVersion) + } - ngVersionProperty.set(neogradleVersions.versions.firstOrNull() ?: emptyVersion) - mdVersionProperty.set(moddevVersions.versions.firstOrNull() ?: emptyVersion) + hasDownloadedVersions = true + + withContext(Dispatchers.Swing) { + uiCallback() } } } diff --git a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt index 28826f429..360c3d2f9 100644 --- a/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ParchmentCreatorProperty.kt @@ -55,7 +55,6 @@ class ParchmentCreatorProperty( override val graphProperty: GraphProperty = graph.property(defaultValue) var versions: ParchmentVersions by graphProperty - private var allParchmentVersions: List? = null private var availableParchmentVersions: List = emptyList() private val useParchmentProperty = graphProperty.transform({ it.use }, { versions.copy(use = it) }) @@ -170,21 +169,12 @@ class ParchmentCreatorProperty( refreshVersionsLists() } - application.executeOnPooledThread { - runBlocking { - val downloadedVersions = ParchmentVersion.downloadData() - - if (downloadedVersions.isNotEmpty()) { - withContext(Dispatchers.Swing) { - allParchmentVersions = downloadedVersions.sortedByDescending(ParchmentVersion::parchmentVersion) - refreshVersionsLists() + downloadVersions { + refreshVersionsLists() - val minecraftVersion = getPlatformMinecraftVersion() - if (minecraftVersion != null) { - mcVersionProperty.set(minecraftVersion) - } - } - } + val minecraftVersion = getPlatformMinecraftVersion() + if (minecraftVersion != null) { + mcVersionProperty.set(minecraftVersion) } } } @@ -253,6 +243,34 @@ class ParchmentCreatorProperty( return SemanticVersion(normalizedVersion) } + + companion object { + + private var hasDownloadedVersions = false + + private var allParchmentVersions: List? = null + + private fun downloadVersions(uiCallback: () -> Unit) { + if (hasDownloadedVersions) { + uiCallback() + return + } + + application.executeOnPooledThread { + runBlocking { + allParchmentVersions = ParchmentVersion.downloadData() + .sortedByDescending(ParchmentVersion::parchmentVersion) + + hasDownloadedVersions = true + + withContext(Dispatchers.Swing) { + uiCallback() + } + } + } + } + } + class Factory : CreatorPropertyFactory { override fun create( descriptor: TemplatePropertyDescriptor, From 55cbc546d6f93934bc225a34ffccd22aabeca87a Mon Sep 17 00:00:00 2001 From: RedNesto Date: Mon, 8 Jul 2024 18:03:21 +0200 Subject: [PATCH 110/118] Remove superfluous blank line --- .../creator/custom/types/NeoForgeVersionsCreatorProperty.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index d33fb96ca..7148fa4af 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -143,7 +143,6 @@ class NeoForgeVersionsCreatorProperty( ngVersionProperty.set(ngVersion?.versions?.firstOrNull() ?: emptyVersion) mdVersionProperty.set(mdVersion?.versions?.firstOrNull() ?: emptyVersion) - } } From cba685f28d513267aeec9519f5c53797f1f474af Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 11 Jul 2024 16:19:42 +0200 Subject: [PATCH 111/118] Actually add the builtin repo by default --- src/main/kotlin/MinecraftSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/MinecraftSettings.kt b/src/main/kotlin/MinecraftSettings.kt index cc7cc00da..0a924aa64 100644 --- a/src/main/kotlin/MinecraftSettings.kt +++ b/src/main/kotlin/MinecraftSettings.kt @@ -42,7 +42,7 @@ class MinecraftSettings : PersistentStateComponent { var isShadowAnnotationsSameLine: Boolean = true, - var creatorTemplateRepos: List = emptyList(), + var creatorTemplateRepos: List = listOf(TemplateRepo.makeBuiltinRepo()), ) @Tag("repo") From 1ce67eb820aabfafdb7681a807454e97dae9983f Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 11 Jul 2024 16:20:03 +0200 Subject: [PATCH 112/118] Hide the repositories row if only one repo is configured --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index a25fe1fbb..df49cf20f 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -123,9 +123,9 @@ class CustomPlatformStep( lateinit var templatePropertyPlaceholder: Placeholder builder.row(MCDevBundle("creator.ui.custom.repos.label")) { - segmentedButton(templateRepos, { it.name }) + segmentedButton(templateRepos) { it.name } .bind(templateRepoProperty) - } + }.visible(templateRepos.size > 1) builder.row { templateProvidersProcessIcon = From 4e61b15017f955c76ad618a56a8b15b0c0a6b693 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 11 Jul 2024 16:59:39 +0200 Subject: [PATCH 113/118] Proper module generation for finalizers --- src/main/kotlin/creator/custom/CustomPlatformStep.kt | 2 +- .../creator/custom/finalizers/CreatorFinalizer.kt | 12 ++++++++---- .../creator/custom/finalizers/GitAddAllFinalizer.kt | 6 +++--- .../finalizers/ImportGradleProjectFinalizer.kt | 10 ++++------ .../custom/finalizers/ImportMavenProjectFinalizer.kt | 10 ++++------ .../custom/finalizers/RunGradleTasksFinalizer.kt | 9 ++++----- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/creator/custom/CustomPlatformStep.kt b/src/main/kotlin/creator/custom/CustomPlatformStep.kt index df49cf20f..8c54b0bf9 100644 --- a/src/main/kotlin/creator/custom/CustomPlatformStep.kt +++ b/src/main/kotlin/creator/custom/CustomPlatformStep.kt @@ -515,7 +515,7 @@ class CustomPlatformStep( val finalizers = selectedTemplate.descriptor.finalizers if (!finalizers.isNullOrEmpty()) { - CreatorFinalizer.executeAll(project, finalizers, templateProperties) + CreatorFinalizer.executeAll(context, finalizers, templateProperties) } } } diff --git a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt index 458c048d2..a65e225b7 100644 --- a/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/CreatorFinalizer.kt @@ -23,11 +23,11 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.creator.custom.TemplateEvaluator import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.TemplateValidationReporterImpl +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.extensions.RequiredElement -import com.intellij.openapi.project.Project import com.intellij.openapi.util.KeyedExtensionCollector import com.intellij.serviceContainer.BaseKeyedLazyInstance import com.intellij.util.KeyedLazyInstance @@ -37,7 +37,7 @@ interface CreatorFinalizer { fun validate(reporter: TemplateValidationReporter, properties: Map) = Unit - fun execute(project: Project, properties: Map, templateProperties: Map) + fun execute(context: WizardContext, properties: Map, templateProperties: Map) companion object { private val EP_NAME = @@ -77,7 +77,11 @@ interface CreatorFinalizer { } } - fun executeAll(project: Project, finalizers: List>, templateProperties: Map) { + fun executeAll( + context: WizardContext, + finalizers: List>, + templateProperties: Map + ) { for ((index, properties) in finalizers.withIndex()) { val type = properties["type"] as String val condition = properties["condition"] as? String @@ -89,7 +93,7 @@ interface CreatorFinalizer { val finalizer = COLLECTOR.findSingle(type)!! try { - finalizer.execute(project, properties, templateProperties) + finalizer.execute(context, properties, templateProperties) } catch (t: Throwable) { if (t is ControlFlowException) { throw t diff --git a/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt index 28ff7316f..ea099c9f8 100644 --- a/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/GitAddAllFinalizer.kt @@ -22,11 +22,11 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.util.ExecUtil -import com.intellij.openapi.project.Project +import com.intellij.ide.util.projectWizard.WizardContext class GitAddAllFinalizer : CreatorFinalizer { - override fun execute(project: Project, properties: Map, templateProperties: Map) { - ExecUtil.execAndGetOutput(GeneralCommandLine("git", "add", ".").withWorkDirectory(project.basePath)) + override fun execute(context: WizardContext, properties: Map, templateProperties: Map) { + ExecUtil.execAndGetOutput(GeneralCommandLine("git", "add", ".").withWorkDirectory(context.projectFileDirectory)) } } diff --git a/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt index 5048f7e22..7385d945e 100644 --- a/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/ImportGradleProjectFinalizer.kt @@ -20,18 +20,16 @@ package com.demonwav.mcdev.creator.custom.finalizers +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.project.Project -import com.intellij.openapi.project.guessProjectDir -import kotlin.io.path.absolutePathString import org.jetbrains.plugins.gradle.service.project.open.canLinkAndRefreshGradleProject import org.jetbrains.plugins.gradle.service.project.open.linkAndRefreshGradleProject class ImportGradleProjectFinalizer : CreatorFinalizer { - override fun execute(project: Project, properties: Map, templateProperties: Map) { - val projectDir = project.guessProjectDir()?.toNioPath()?.absolutePathString() - ?: return + override fun execute(context: WizardContext, properties: Map, templateProperties: Map) { + val project = context.project!! + val projectDir = context.projectFileDirectory val canLink = canLinkAndRefreshGradleProject(projectDir, project, showValidationDialog = false) thisLogger().info("canLink = $canLink projectDir = $projectDir") if (canLink) { diff --git a/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt index 78e565fbc..fb0652c57 100644 --- a/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/ImportMavenProjectFinalizer.kt @@ -21,20 +21,18 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.util.invokeAndWait +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.project.Project -import com.intellij.openapi.project.guessProjectDir import com.intellij.openapi.vfs.VfsUtil import java.nio.file.Path import java.util.concurrent.TimeUnit -import kotlin.io.path.absolutePathString import org.jetbrains.idea.maven.project.importing.MavenImportingManager class ImportMavenProjectFinalizer : CreatorFinalizer { - override fun execute(project: Project, properties: Map, templateProperties: Map) { - val projectDir = project.guessProjectDir()?.toNioPath()?.absolutePathString() - ?: return + override fun execute(context: WizardContext, properties: Map, templateProperties: Map) { + val project = context.project!! + val projectDir = context.projectFileDirectory val pomFile = VfsUtil.findFile(Path.of(projectDir).resolve("pom.xml"), true) ?: return diff --git a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt index 419a7f84f..9d919d1f9 100644 --- a/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt +++ b/src/main/kotlin/creator/custom/finalizers/RunGradleTasksFinalizer.kt @@ -22,9 +22,8 @@ package com.demonwav.mcdev.creator.custom.finalizers import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.util.runGradleTaskAndWait +import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.project.Project -import com.intellij.openapi.project.guessProjectDir class RunGradleTasksFinalizer : CreatorFinalizer { @@ -39,11 +38,11 @@ class RunGradleTasksFinalizer : CreatorFinalizer { } } - override fun execute(project: Project, properties: Map, templateProperties: Map) { + override fun execute(context: WizardContext, properties: Map, templateProperties: Map) { @Suppress("UNCHECKED_CAST") val tasks = properties["tasks"] as List - val projectDir = project.guessProjectDir()?.toNioPath() - ?: return thisLogger().error("Could not find project dir") + val project = context.project!! + val projectDir = context.projectDirectory thisLogger().info("tasks = $tasks projectDir = $projectDir") runGradleTaskAndWait(project, projectDir) { settings -> From c6ad8d0228c4100531224a7a26dbbfc0af9b7456 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Thu, 11 Jul 2024 17:19:36 +0200 Subject: [PATCH 114/118] Update templates submodule --- templates | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates b/templates index e238cceee..c8cf7b83d 160000 --- a/templates +++ b/templates @@ -1 +1 @@ -Subproject commit e238cceeef0e3eba238e045c1c56b2cd26662a14 +Subproject commit c8cf7b83d9f15903c40e603725318de5bcba85f8 From d61a9bc2e9ad54d24951b17df068356528799da6 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 12 Jul 2024 11:29:48 +0200 Subject: [PATCH 115/118] Add customizable storage keys --- .../kotlin/creator/custom/TemplateDescriptor.kt | 2 +- .../creator/custom/types/CreatorProperty.kt | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/creator/custom/TemplateDescriptor.kt b/src/main/kotlin/creator/custom/TemplateDescriptor.kt index d8aafc13d..abed981b6 100644 --- a/src/main/kotlin/creator/custom/TemplateDescriptor.kt +++ b/src/main/kotlin/creator/custom/TemplateDescriptor.kt @@ -54,7 +54,7 @@ data class TemplatePropertyDescriptor( val maxSegmentedButtonsCount: Int? = null, val forceDropdown: Boolean? = null, val groupProperties: List? = null, - val remember: Boolean? = null, + val remember: Any? = null, val visible: Any? = null, val editable: Boolean? = null, val collapsible: Boolean? = null, diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index 0de0db84b..d4e710ed8 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -124,7 +124,18 @@ abstract class CreatorProperty( */ open fun setupProperty(reporter: TemplateValidationReporter) { if (descriptor.remember != false && descriptor.derives == null) { - toStringProperty(graphProperty).bindStorage(makeStorageKey()) + val storageKey = when (val remember = descriptor.remember) { + null, true -> makeStorageKey() + is String -> makeCustomStorageKey(remember) + else -> { + reporter.error("Invalid 'remember' value. Must be a boolean or a string") + null + } + } + + if (storageKey != null) { + toStringProperty(graphProperty).bindStorage(storageKey) + } } visibleProperty = setupVisibleProperty(reporter, descriptor.visible) @@ -181,6 +192,10 @@ abstract class CreatorProperty( return "$base.$discriminator" } + protected fun makeCustomStorageKey(key: String): String { + return "${javaClass.name}.property.$key" + } + protected fun collectPropertiesValues(names: List? = null): MutableMap { val into = mutableMapOf() From 4b637f7f65f147ae3adf71df0a4febf3f82ed766 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 12 Jul 2024 11:32:16 +0200 Subject: [PATCH 116/118] Rename FabricApi -> Fabric API and ArchitecturyApi -> Architectury API --- .../custom/types/ArchitecturyVersionsCreatorProperty.kt | 8 ++++---- .../resources/messages/MinecraftDevelopment.properties | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt index dc73df32c..ec15fb12c 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt @@ -219,7 +219,7 @@ class ArchitecturyVersionsCreatorProperty( // .component.foreground = JBColor.YELLOW // }.enabled(descriptor.editable != false) - panel.row("FabricApi Version:") { + panel.row("Fabric API Version:") { comboBox(fabricApiVersionModel) .bindItem(fabricApiVersionProperty) .enabledIf(useFabricApiVersionProperty) @@ -227,14 +227,14 @@ class ArchitecturyVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - checkBox("Use FabricApi") + checkBox("Use Fabric API") .bindSelected(useFabricApiVersionProperty) label("Unable to match API versions to Minecraft version") .visibleIf(fabricApiHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW } - panel.row("ArchitecturyApi Version:") { + panel.row("Architectury API Version:") { comboBox(architecturyApiVersionModel) .bindItem(architecturyApiVersionProperty) .enabledIf(useArchitecturyApiVersionProperty) @@ -242,7 +242,7 @@ class ArchitecturyVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } - checkBox("Use ArchitecturyApi") + checkBox("Use Architectury API") .bindSelected(useArchitecturyApiVersionProperty) label("Unable to match API versions to Minecraft version") .visibleIf(architecturyApiHasMatchingGameVersion.not()) diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 5753b9c93..2296846bb 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -96,8 +96,8 @@ creator.ui.loom_version.label=Loom Version: creator.ui.loader_version.label=Loader Version: creator.ui.yarn_version.label=Yarn Version: creator.ui.use_official_mappings.label=Use official mappings -creator.ui.fabricapi_version.label=FabricApi Version: -creator.ui.use_fabricapi.label=Use FabricApi +creator.ui.fabricapi_version.label=Fabric API Version: +creator.ui.use_fabricapi.label=Use Fabric API creator.ui.spongeapi_version.label=Sponge Version: creator.ui.velocity_version.label=Velocity Version: From 36b34ae6c52bd8d93f196dda9648e218e2e759bf Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 12 Jul 2024 11:33:39 +0200 Subject: [PATCH 117/118] Remove dead code --- .../creator/custom/types/CreatorProperty.kt | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/main/kotlin/creator/custom/types/CreatorProperty.kt b/src/main/kotlin/creator/custom/types/CreatorProperty.kt index d4e710ed8..3e9e1845c 100644 --- a/src/main/kotlin/creator/custom/types/CreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/CreatorProperty.kt @@ -27,17 +27,14 @@ import com.demonwav.mcdev.creator.custom.TemplateValidationReporter import com.demonwav.mcdev.creator.custom.derivation.PreparedDerivation import com.demonwav.mcdev.creator.custom.derivation.SelectPropertyDerivation import com.intellij.ide.util.projectWizard.WizardContext -import com.intellij.openapi.diagnostic.getOrLogException import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.observable.util.bindStorage import com.intellij.openapi.observable.util.transform -import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Row -import com.intellij.ui.dsl.builder.bindItem abstract class CreatorProperty( val descriptor: TemplatePropertyDescriptor, @@ -98,17 +95,6 @@ abstract class CreatorProperty( protected open fun convertSelectDerivationResult(original: Any?): Any? = original - fun deriveSelectFirst(parentValues: List, derivation: PropertyDerivation): Any? { - val properties = parentValues.mapIndexed { i, value -> derivation.parents!![i] to value }.toMap() - for (select in derivation.select ?: emptyList()) { - if (TemplateEvaluator.condition(properties, select.condition).getOrLogException(thisLogger()) == true) { - return select.value - } - } - - return derivation.default - } - abstract fun buildUi(panel: Panel, context: WizardContext) /** @@ -299,13 +285,4 @@ abstract class CreatorProperty( return prop } - - protected fun Panel.buildDropdownUi(options: List, graphProp: GraphProperty) { - row(descriptor.translatedLabel) { - comboBox(options) - .bindItem(graphProp) - .enabled(descriptor.editable != false) - .also { ComboboxSpeedSearch.installOn(it.component) } - }.propertyVisibility() - } } From e5e583213a14cb8b92f7efbe9d3d01517ed44a26 Mon Sep 17 00:00:00 2001 From: RedNesto Date: Fri, 12 Jul 2024 12:01:14 +0200 Subject: [PATCH 118/118] Add versions download indicator --- .../ArchitecturyVersionsCreatorProperty.kt | 18 +++++++++++++++++- .../types/FabricVersionsCreatorProperty.kt | 14 ++++++++++++++ .../types/ForgeVersionsCreatorProperty.kt | 11 +++++++++++ .../types/NeoForgeVersionsCreatorProperty.kt | 11 +++++++++++ .../messages/MinecraftDevelopment.properties | 1 + 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt index ec15fb12c..1c742e80e 100644 --- a/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ArchitecturyVersionsCreatorProperty.kt @@ -20,6 +20,8 @@ package com.demonwav.mcdev.creator.custom.types +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.asset.MCDevBundle.invoke import com.demonwav.mcdev.creator.collectMavenVersions import com.demonwav.mcdev.creator.custom.BuiltinValidations import com.demonwav.mcdev.creator.custom.TemplateEvaluator @@ -44,6 +46,7 @@ import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitAll @@ -73,6 +76,7 @@ class ArchitecturyVersionsCreatorProperty( ) private val defaultValue = createDefaultValue(descriptor.default) + private val loadingVersionsProperty = graph.property(true) override val graphProperty: GraphProperty = graph.property(defaultValue) var model: ArchitecturyVersionsModel by graphProperty @@ -161,6 +165,11 @@ class ArchitecturyVersionsCreatorProperty( } override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("") { + cell(AsyncProcessIcon("ArchitecturyVersions download")) + label(MCDevBundle("creator.ui.versions_download.label")) + }.visibleIf(loadingVersionsProperty) + panel.row("Minecraft Version:") { comboBox(mcVersionModel) .bindItem(mcVersionProperty) @@ -168,6 +177,7 @@ class ArchitecturyVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row("Forge Version:") { comboBox(forgeVersionsModel) @@ -176,6 +186,7 @@ class ArchitecturyVersionsCreatorProperty( .also { ComboboxSpeedSearch.installOn(it.component) } .component }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row("NeoForge Version:") { comboBox(nfVersionsModel) @@ -184,6 +195,7 @@ class ArchitecturyVersionsCreatorProperty( .also { ComboboxSpeedSearch.installOn(it.component) } .component }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) // // panel.row("Loom Version:") { @@ -201,6 +213,7 @@ class ArchitecturyVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) // Official mappings forced currently, yarn mappings are not handled yet // panel.row("Yarn Version:") { @@ -232,7 +245,7 @@ class ArchitecturyVersionsCreatorProperty( label("Unable to match API versions to Minecraft version") .visibleIf(fabricApiHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW - } + }.visibleIf(!loadingVersionsProperty) panel.row("Architectury API Version:") { comboBox(architecturyApiVersionModel) @@ -248,6 +261,7 @@ class ArchitecturyVersionsCreatorProperty( .visibleIf(architecturyApiHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) } override fun setupProperty(reporter: TemplateValidationReporter) { @@ -288,6 +302,8 @@ class ArchitecturyVersionsCreatorProperty( } updateMcVersionsList() + + loadingVersionsProperty.set(false) } } diff --git a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt index d2fc11222..870c470cb 100644 --- a/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/FabricVersionsCreatorProperty.kt @@ -43,6 +43,7 @@ import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.bindItem import com.intellij.ui.dsl.builder.bindSelected import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitAll @@ -68,6 +69,7 @@ class FabricVersionsCreatorProperty( ) private val defaultValue = createDefaultValue(descriptor.default) + private val loadingVersionsProperty = graph.property(true) override val graphProperty: GraphProperty = graph.property(defaultValue) var model: FabricVersionsModel by graphProperty @@ -134,6 +136,11 @@ class FabricVersionsCreatorProperty( } override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("") { + cell(AsyncProcessIcon("FabricVersions download")) + label(MCDevBundle("creator.ui.versions_download.label")) + }.visibleIf(loadingVersionsProperty) + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionModel) .bindItem(mcVersionProperty) @@ -145,6 +152,7 @@ class FabricVersionsCreatorProperty( checkBox(MCDevBundle("creator.ui.show_snapshots.label")) .bindSelected(showMcSnapshotsProperty) }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row(MCDevBundle("creator.ui.loom_version.label")) { comboBox(loomVersionModel) @@ -153,6 +161,7 @@ class FabricVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row(MCDevBundle("creator.ui.loader_version.label")) { comboBox(loaderVersionModel) @@ -161,6 +170,7 @@ class FabricVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row(MCDevBundle("creator.ui.yarn_version.label")) { comboBox(yarnVersionModel) @@ -177,6 +187,7 @@ class FabricVersionsCreatorProperty( .visibleIf(yarnHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) panel.row(MCDevBundle("creator.ui.fabricapi_version.label")) { comboBox(fabricApiVersionModel) @@ -192,6 +203,7 @@ class FabricVersionsCreatorProperty( .visibleIf(fabricApiHasMatchingGameVersion.not()) .component.foreground = JBColor.YELLOW }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) } override fun setupProperty(reporter: TemplateValidationReporter) { @@ -230,6 +242,8 @@ class FabricVersionsCreatorProperty( loomVersionProperty.set(defaultValue) } + + loadingVersionsProperty.set(false) } } diff --git a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt index a20c95da1..de3464fae 100644 --- a/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/ForgeVersionsCreatorProperty.kt @@ -31,12 +31,14 @@ import com.demonwav.mcdev.util.SemanticVersion import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.not import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon import javax.swing.DefaultComboBoxModel import kotlin.collections.Map import kotlinx.coroutines.Dispatchers @@ -54,6 +56,7 @@ class ForgeVersionsCreatorProperty( private val defaultValue = createDefaultValue(descriptor.default) + private val loadingVersionsProperty = graph.property(true) override val graphProperty: GraphProperty = graph.property(defaultValue) var versions: ForgeVersions by graphProperty @@ -90,6 +93,11 @@ class ForgeVersionsCreatorProperty( } override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("") { + cell(AsyncProcessIcon("ForgeVersions download")) + label(MCDevBundle("creator.ui.versions_download.label")) + }.visibleIf(loadingVersionsProperty) + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionsModel) .bindItem(mcVersionProperty) @@ -104,6 +112,7 @@ class ForgeVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) } override fun setupProperty(reporter: TemplateValidationReporter) { @@ -144,6 +153,8 @@ class ForgeVersionsCreatorProperty( downloadVersions { reloadMinecraftVersions() + + loadingVersionsProperty.set(false) } } diff --git a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt index 7148fa4af..10925897d 100644 --- a/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt +++ b/src/main/kotlin/creator/custom/types/NeoForgeVersionsCreatorProperty.kt @@ -34,12 +34,14 @@ import com.demonwav.mcdev.util.asyncIO import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.observable.properties.PropertyGraph +import com.intellij.openapi.observable.util.not import com.intellij.openapi.observable.util.transform import com.intellij.ui.ComboboxSpeedSearch import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.RightGap import com.intellij.ui.dsl.builder.bindItem import com.intellij.util.application +import com.intellij.util.ui.AsyncProcessIcon import javax.swing.DefaultComboBoxModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.awaitAll @@ -57,6 +59,7 @@ class NeoForgeVersionsCreatorProperty( private val defaultValue = createDefaultValue(descriptor.default) + private val loadingVersionsProperty = graph.property(true) override val graphProperty: GraphProperty = graph.property(defaultValue) var versions: NeoForgeVersions by graphProperty @@ -95,6 +98,11 @@ class NeoForgeVersionsCreatorProperty( } override fun buildUi(panel: Panel, context: WizardContext) { + panel.row("") { + cell(AsyncProcessIcon("NeoForgeVersions download")) + label(MCDevBundle("creator.ui.versions_download.label")) + }.visibleIf(loadingVersionsProperty) + panel.row(MCDevBundle("creator.ui.mc_version.label")) { comboBox(mcVersionsModel) .bindItem(mcVersionProperty) @@ -109,6 +117,7 @@ class NeoForgeVersionsCreatorProperty( .validationOnApply(BuiltinValidations.nonEmptyVersion) .also { ComboboxSpeedSearch.installOn(it.component) } }.enabled(descriptor.editable != false) + .visibleIf(!loadingVersionsProperty) } override fun setupProperty(reporter: TemplateValidationReporter) { @@ -143,6 +152,8 @@ class NeoForgeVersionsCreatorProperty( ngVersionProperty.set(ngVersion?.versions?.firstOrNull() ?: emptyVersion) mdVersionProperty.set(mdVersion?.versions?.firstOrNull() ?: emptyVersion) + + loadingVersionsProperty.set(false) } } diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 2296846bb..2ee0ef24d 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -100,6 +100,7 @@ creator.ui.fabricapi_version.label=Fabric API Version: creator.ui.use_fabricapi.label=Use Fabric API creator.ui.spongeapi_version.label=Sponge Version: creator.ui.velocity_version.label=Velocity Version: +creator.ui.versions_download.label=Downloading versions... creator.ui.warn.no_yarn_to_mc_match=Unable to match Yarn versions to Minecraft version creator.ui.warn.no_fabricapi_to_mc_match=Unable to match API versions to Minecraft version