User: rednesto Date: 10 Aug 24 14:10 Revision: 255120dc1aaef3ee3c349b2c0f2df4c0187067ca Summary: Merge branch '2023.2' into 2023.3 TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9584&personal=false Index: gradle.properties =================================================================== --- gradle.properties (revision 370a370b2e6ac80765da5ced9b6c3adfdd6e2e09) +++ gradle.properties (revision 255120dc1aaef3ee3c349b2c0f2df4c0187067ca) @@ -24,7 +24,7 @@ ideaVersion = 2023.3 ideaVersionName = 2023.3 -coreVersion = 1.8.0 +coreVersion = 1.8.1 downloadIdeaSources = true pluginTomlVersion = 233.11799.172 Index: src/main/kotlin/creator/custom/CreatorContext.kt =================================================================== --- src/main/kotlin/creator/custom/CreatorContext.kt (revision 370a370b2e6ac80765da5ced9b6c3adfdd6e2e09) +++ src/main/kotlin/creator/custom/CreatorContext.kt (revision 255120dc1aaef3ee3c349b2c0f2df4c0187067ca) @@ -54,4 +54,7 @@ * A general purpose scope dependent of the main creator scope, cancelled when the creator is closed. */ fun childScope(name: String): CoroutineScope = scope.namedChildScope(name) + + @Suppress("UNCHECKED_CAST") + fun property(name: String): CreatorProperty = properties[name] as CreatorProperty } Index: src/main/kotlin/creator/custom/CustomPlatformStep.kt =================================================================== --- src/main/kotlin/creator/custom/CustomPlatformStep.kt (revision 370a370b2e6ac80765da5ced9b6c3adfdd6e2e09) +++ src/main/kotlin/creator/custom/CustomPlatformStep.kt (revision 255120dc1aaef3ee3c349b2c0f2df4c0187067ca) @@ -22,43 +22,23 @@ 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.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.CodeInsightSettings -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 import com.intellij.ide.wizard.NewProjectWizardStep import com.intellij.openapi.application.EDT -import com.intellij.openapi.application.WriteAction import com.intellij.openapi.application.asContextElement -import com.intellij.openapi.diagnostic.Attachment -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.properties.GraphProperty import com.intellij.openapi.observable.util.transform import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.util.Disposer -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 @@ -67,17 +47,11 @@ 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.JLabel import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.collections.set -import kotlin.io.path.createDirectories -import kotlin.io.path.writeText import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel @@ -124,10 +98,16 @@ lateinit var noTemplatesAvailable: Cell var templateLoadingJob: Job? = null - private var hasTemplateErrors: Boolean = true + private val externalPropertyProvider = object : ExternalTemplatePropertyProvider { + override val projectNameProperty: GraphProperty + get() = data.getUserData(NewProjectWizardBaseData.KEY)?.nameProperty + ?: throw RuntimeException("Could not find wizard base data") - private var properties = mutableMapOf>() - private var creatorContext = CreatorContext(propertyGraph, properties, context, creatorScope) + override val useGit: Boolean + get() = data.getUserData(GitNewProjectWizardData.KEY)?.git == true + } + private val templateProcessor = + CreatorTemplateProcessor(propertyGraph, context, creatorScope, externalPropertyProvider) init { Disposer.register(context.disposable) { @@ -178,7 +158,7 @@ toolTipText = template.tooltip }.bind(selectedTemplateProperty) .validation { - addApplyRule("", condition = ::hasTemplateErrors) + addApplyRule("", condition = templateProcessor::hasTemplateErrors) } }.visibleIf( availableTemplatesProperty.transform { it.size > 1 } @@ -203,7 +183,7 @@ } selectedTemplateProperty.afterChange { template -> - createOptionsPanelInBackground(template, templatePropertyPlaceholder) + templatePropertyPlaceholder.component = templateProcessor.createOptionsPanel(template) } builder.row { @@ -290,276 +270,7 @@ } } - private fun createOptionsPanelInBackground(template: LoadedTemplate, placeholder: Placeholder) { - properties = mutableMapOf() - creatorContext = creatorContext.copy(properties = properties) - - if (!template.isValid) { - return - } - - val baseData = data.getUserData(NewProjectWizardBaseData.KEY) - ?: return thisLogger().error("Could not find wizard base data") - - properties["PROJECT_NAME"] = ExternalCreatorProperty( - context = creatorContext, - graphProperty = baseData.nameProperty, - valueType = String::class.java - ) - - placeholder.component = panel { - 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 { - hasTemplateErrors = reporter.hasErrors - reporter.display(this) - - if (!reporter.hasErrors) { - for (uiFactory in uiFactories) { - uiFactory.accept(this) - } - } - } - } - } - - private fun setupTemplate( - template: LoadedTemplate, - reporter: TemplateValidationReporterImpl - ): List> { - return try { - 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( - "Unexpected error during template setup", - t, - template.label, - template.descriptor.toString() - ) - - emptyList() - } finally { - reporter.subject = null - } - } - - private fun setupProperty( - descriptor: TemplatePropertyDescriptor, - reporter: TemplateValidationReporter - ): Pair, Int>? { - if (!descriptor.groupProperties.isNullOrEmpty()) { - val childrenUiFactories = descriptor.groupProperties - .mapNotNull { setupProperty(it, reporter) } - .sortedBy { (_, order) -> order } - .map { it.first } - - val factory = Consumer { panel -> - val label = descriptor.translatedLabel - if (descriptor.collapsible == false) { - panel.group(label) { - for (childFactory in childrenUiFactories) { - childFactory.accept(this@group) - } - } - } else { - val group = panel.collapsibleGroup(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) { - reporter.fatal("Duplicate property name ${descriptor.name}") - } - - val prop = CreatorPropertyFactory.createFromType(descriptor.type, descriptor, creatorContext) - if (prop == null) { - reporter.fatal("Unknown template property type ${descriptor.type}") - } - - prop.setupProperty(reporter) - - properties[descriptor.name] = prop - - if (descriptor.visible == false) { - return null - } - - val factory = Consumer { panel -> prop.buildUi(panel) } - val order = descriptor.order ?: 0 - return factory to order - } - override fun setupProject(project: Project) { - val template = selectedTemplate - if (template is EmptyLoadedTemplate) { - return + templateProcessor.generateFiles(project, selectedTemplate) - } + } - - val projectPath = context.projectDirectory - 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 } - ) { - continue - } +} - - val relativeTemplate = TemplateEvaluator.template(templateProperties, file.template).getOrNull() - ?: continue - val relativeDest = TemplateEvaluator.template(templateProperties, file.destination).getOrNull() - ?: 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 - } - - var fileTemplateProperties = templateProperties - if (file.properties != null) { - fileTemplateProperties = templateProperties.toMutableMap() - fileTemplateProperties.putAll(file.properties) - } - - val processedContent = TemplateEvaluator.template(fileTemplateProperties, templateContents) - .onFailure { t -> - val attachment = Attachment(relativeTemplate, templateContents) - thisLogger().error("Failed evaluate template '$relativeTemplate'", t, attachment) - } - .getOrNull() - ?: continue - - 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 - } - - thisLogger().error("Failed to process template file $file", t) - } - } - - val finalizeAction = { - WriteAction.runAndWait { - LocalFileSystem.getInstance().refresh(false) - // Apparently a module root is required for the reformat to work - setupTempRootModule(project, projectPath) - - reformatFiles(project, generatedFiles) - openFilesInEditor(project, generatedFiles) - } - - val finalizers = selectedTemplate.descriptor.finalizers - if (!finalizers.isNullOrEmpty()) { - CreatorFinalizer.executeAll(context, project, finalizers, templateProperties) - } - } - if (context.isCreatingNewProject) { - TemplateService.instance.registerFinalizerAction(project, finalizeAction) - } else { - application.executeOnPooledThread { finalizeAction() } - } - } - - 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() - - into.putAll(TemplateEvaluator.baseProperties) - - val gitData = data.getUserData(GitNewProjectWizardData.KEY) - into["USE_GIT"] = gitData?.git == true - - 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) } - - val processor = ReformatCodeProcessor(project, psiFiles.toTypedArray(), null, false) - psiFiles.forEach(processor::setDoNotKeepLineBreaks) - - val insightSettings = CodeInsightSettings.getInstance() - val oldSecondReformat = insightSettings.ENABLE_SECOND_REFORMAT - insightSettings.ENABLE_SECOND_REFORMAT = true - try { - processor.run() - } finally { - insightSettings.ENABLE_SECOND_REFORMAT = oldSecondReformat - } - } - - 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) - } - } - } -}