User: rednesto
Date: 30 Jul 24 11:59
Revision: 7198d5f7c00898e8f561a29725279d1f85bcef27
Summary:
Support Kotlin event listener generation
TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=9517&personal=false
Index: changelog.md
===================================================================
--- changelog.md (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ changelog.md (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -5,6 +5,7 @@
### Added
- Access widener completion in fabric.mod.json
+- Event listener generation for Kotlin
### Fixed
Index: src/main/kotlin/insight/generation/EventGenHelper.kt
===================================================================
--- src/main/kotlin/insight/generation/EventGenHelper.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/insight/generation/EventGenHelper.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,122 @@
+/*
+ * 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.insight.generation
+
+import com.demonwav.mcdev.util.addImplements
+import com.intellij.core.CoreJavaCodeStyleManager
+import com.intellij.lang.LanguageExtension
+import com.intellij.lang.LanguageExtensionPoint
+import com.intellij.openapi.editor.RangeMarker
+import com.intellij.openapi.extensions.ExtensionPointName
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.codeStyle.CodeStyleManager
+import com.intellij.psi.util.parentOfType
+import org.jetbrains.kotlin.idea.core.ShortenReferences
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtPsiFactory
+
+interface EventGenHelper {
+
+ fun addImplements(context: PsiElement, fqn: String)
+
+ fun reformatAndShortenRefs(file: PsiFile, startOffset: Int, endOffset: Int)
+
+ companion object {
+
+ val EP_NAME = ExtensionPointName.create>(
+ "com.demonwav.minecraft-dev.eventGenHelper"
+ )
+ val COLLECTOR = LanguageExtension(EP_NAME, JvmEventGenHelper())
+ }
+}
+
+open class JvmEventGenHelper : EventGenHelper {
+
+ override fun addImplements(context: PsiElement, fqn: String) {}
+
+ override fun reformatAndShortenRefs(file: PsiFile, startOffset: Int, endOffset: Int) {
+ val project = file.project
+
+ val marker = doReformat(project, file, startOffset, endOffset) ?: return
+
+ CoreJavaCodeStyleManager.getInstance(project).shortenClassReferences(file, marker.startOffset, marker.endOffset)
+ }
+
+ companion object {
+
+ fun doReformat(project: Project, file: PsiFile, startOffset: Int, endOffset: Int): RangeMarker? {
+ val documentManager = PsiDocumentManager.getInstance(project)
+ val document = documentManager.getDocument(file) ?: return null
+
+ val marker = document.createRangeMarker(startOffset, endOffset).apply {
+ isGreedyToLeft = true
+ isGreedyToRight = true
+ }
+
+ CodeStyleManager.getInstance(project).reformatText(file, startOffset, endOffset)
+ documentManager.commitDocument(document)
+
+ return marker
+ }
+ }
+}
+
+class JavaEventGenHelper : JvmEventGenHelper() {
+
+ override fun addImplements(context: PsiElement, fqn: String) {
+ val psiClass = context.parentOfType(true) ?: return
+ psiClass.addImplements(fqn)
+ }
+}
+
+class KotlinEventGenHelper : EventGenHelper {
+
+ private fun hasSuperType(ktClass: KtClassOrObject, fqn: String): Boolean {
+ val names = setOf(fqn, fqn.substringAfterLast('.'))
+ return ktClass.superTypeListEntries.any { it.text in names }
+ }
+
+ override fun addImplements(context: PsiElement, fqn: String) {
+ val ktClass = context.parentOfType(true) ?: return
+ if (hasSuperType(ktClass, fqn)) {
+ return
+ }
+
+ val factory = KtPsiFactory.contextual(context)
+ val entry = factory.createSuperTypeEntry(fqn)
+ val insertedEntry = ktClass.addSuperTypeListEntry(entry)
+ ShortenReferences.DEFAULT.process(insertedEntry)
+ }
+
+ override fun reformatAndShortenRefs(file: PsiFile, startOffset: Int, endOffset: Int) {
+ file as? KtFile ?: return
+ val project = file.project
+
+ val marker = JvmEventGenHelper.doReformat(project, file, startOffset, endOffset) ?: return
+
+ ShortenReferences.DEFAULT.process(file, marker.startOffset, marker.endOffset)
+ }
+}
Index: src/main/kotlin/insight/generation/EventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/insight/generation/EventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/insight/generation/EventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -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.insight.generation
+
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+
+interface EventListenerGenerationSupport {
+
+ fun canGenerate(context: PsiElement, editor: Editor): Boolean
+
+ fun generateEventListener(
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ )
+}
Index: src/main/kotlin/insight/generation/GenerateEventListenerAction.kt
===================================================================
--- src/main/kotlin/insight/generation/GenerateEventListenerAction.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/insight/generation/GenerateEventListenerAction.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -21,13 +21,35 @@
package com.demonwav.mcdev.insight.generation
import com.demonwav.mcdev.asset.MCDevBundle
-import com.intellij.codeInsight.generation.actions.BaseGenerateAction
+import com.demonwav.mcdev.facet.MinecraftFacet
+import com.demonwav.mcdev.util.findModule
+import com.intellij.codeInsight.CodeInsightActionHandler
+import com.intellij.codeInsight.actions.CodeInsightAction
import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiFile
-class GenerateEventListenerAction : BaseGenerateAction(GenerateEventListenerHandler()) {
+class GenerateEventListenerAction : CodeInsightAction() {
+ private val handler = GenerateEventListenerHandler()
+
+ override fun getHandler(): CodeInsightActionHandler = handler
+
override fun update(e: AnActionEvent) {
super.update(e)
e.presentation.text = MCDevBundle("generate.event_listener.title")
}
+
+ override fun isValidForFile(
+ project: Project,
+ editor: Editor,
+ file: PsiFile
+ ): Boolean {
+ val module = file.findModule() ?: return false
+ val minecraftFacet = MinecraftFacet.getInstance(module) ?: return false
+ val support = minecraftFacet.modules.firstNotNullOfOrNull { it.eventListenerGenSupport } ?: return false
+ val caretElement = file.findElementAt(editor.caretModel.offset) ?: return false
+ return support.canGenerate(caretElement, editor)
-}
+ }
+}
Index: src/main/kotlin/insight/generation/GenerateEventListenerHandler.kt
===================================================================
--- src/main/kotlin/insight/generation/GenerateEventListenerHandler.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/insight/generation/GenerateEventListenerHandler.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -20,72 +20,45 @@
package com.demonwav.mcdev.insight.generation
-import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.facet.MinecraftFacet
import com.demonwav.mcdev.insight.generation.ui.EventGenerationDialog
import com.demonwav.mcdev.platform.AbstractModule
-import com.demonwav.mcdev.util.castNotNull
-import com.intellij.codeInsight.generation.ClassMember
-import com.intellij.codeInsight.generation.GenerateMembersHandlerBase
-import com.intellij.codeInsight.generation.GenerationInfo
-import com.intellij.codeInsight.generation.PsiGenerationInfo
+import com.demonwav.mcdev.util.findModule
+import com.demonwav.mcdev.util.mapFirstNotNull
+import com.intellij.codeInsight.CodeInsightActionHandler
import com.intellij.ide.util.TreeClassChooserFactory
-import com.intellij.openapi.actionSystem.DataContext
-import com.intellij.openapi.editor.CaretModel
import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.editor.LogicalPosition
-import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiFile
-import com.intellij.psi.PsiMethod
import com.intellij.psi.search.GlobalSearchScope
-import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.RefactoringBundle
-/**
- * The standard handler to generate a new event listener as a method.
- * Note that this is a psuedo generator as it relies on a wizard and the
- * [.cleanup] to complete
- */
-class GenerateEventListenerHandler : GenerateMembersHandlerBase(MCDevBundle("generate.event_listener.title")) {
+class GenerateEventListenerHandler : CodeInsightActionHandler {
- private data class GenerateData(
- var editor: Editor,
- var position: LogicalPosition,
- var method: PsiMethod?,
- var model: CaretModel,
- var data: GenerationData?,
- var chosenClass: PsiClass,
- var chosenName: String,
- var relevantModule: AbstractModule,
- )
+ override fun invoke(project: Project, editor: Editor, file: PsiFile) {
+ val module = file.findModule() ?: return
+ val facet = MinecraftFacet.getInstance(module) ?: return
+ val eventListenerGenSupport = facet.modules.mapFirstNotNull { it.eventListenerGenSupport } ?: return
+ val caretElement = file.findElementAt(editor.caretModel.offset) ?: return
+ val context = caretElement.context ?: return
- private var data: GenerateData? = null
-
- override fun getHelpId() = "Generate Event Listener Dialog"
-
- override fun chooseOriginalMembers(aClass: PsiClass, project: Project, editor: Editor): Array? {
- val moduleForPsiElement = ModuleUtilCore.findModuleForPsiElement(aClass) ?: return null
-
- val facet = MinecraftFacet.getInstance(moduleForPsiElement) ?: return null
-
val chooser = TreeClassChooserFactory.getInstance(project)
.createWithInnerClassesScopeChooser(
RefactoringBundle.message("choose.destination.class"),
- GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(moduleForPsiElement, false),
+ GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false),
{ aClass1 -> isSuperEventListenerAllowed(aClass1, facet) },
null,
)
chooser.showDialog()
- val chosenClass = chooser.selected ?: return null
+ val chosenClass = chooser.selected ?: return
val relevantModule = facet.modules.asSequence()
.filter { m -> isSuperEventListenerAllowed(chosenClass, m) }
- .firstOrNull() ?: return null
+ .firstOrNull() ?: return
- val chosenClassName = chosenClass.nameIdentifier?.text ?: return null
+ val chosenClassName = chosenClass.nameIdentifier?.text ?: return
val generationDialog = EventGenerationDialog(
editor,
@@ -94,72 +67,21 @@
relevantModule.moduleType.getDefaultListenerName(chosenClass),
)
- val okay = generationDialog.showAndGet()
-
- if (!okay) {
- return null
+ if (!generationDialog.showAndGet()) {
+ return
}
- val dialogDAta = generationDialog.data
- val chosenName = generationDialog.chosenName
-
- val position = editor.caretModel.logicalPosition
-
- val method = PsiTreeUtil.getParentOfType(
- aClass.containingFile.findElementAt(editor.caretModel.offset),
- PsiMethod::class.java,
- )
-
- this.data = GenerateData(
- editor,
- position,
- method,
- editor.caretModel,
- dialogDAta,
+ eventListenerGenSupport.generateEventListener(
+ context,
+ generationDialog.chosenName,
chosenClass,
- chosenName,
- relevantModule,
+ generationDialog.data,
+ editor
)
-
- return DUMMY_RESULT
}
- override fun getAllOriginalMembers(aClass: PsiClass) = null
+ override fun startInWriteAction(): Boolean = false
- override fun generateMemberPrototypes(aClass: PsiClass, originalMember: ClassMember?): Array? {
- if (data == null) {
- return null
- }
-
- data?.let { data ->
- data.relevantModule.doPreEventGenerate(aClass, data.data)
-
- data.model.moveToLogicalPosition(data.position)
-
- val newMethod =
- data.relevantModule.generateEventListenerMethod(aClass, data.chosenClass, data.chosenName, data.data)
-
- if (newMethod != null) {
- val info = PsiGenerationInfo(newMethod)
- info.positionCaret(data.editor, true)
- if (data.method != null) {
- info.insert(aClass, data.method, false)
- }
-
- return arrayOf(info)
- }
- }
-
- return null
- }
-
- override fun isAvailableForQuickList(editor: Editor, file: PsiFile, dataContext: DataContext): Boolean {
- val module = ModuleUtilCore.findModuleForPsiElement(file) ?: return false
-
- val instance = MinecraftFacet.getInstance(module)
- return instance != null && instance.isEventGenAvailable
- }
-
private fun isSuperEventListenerAllowed(eventClass: PsiClass, module: AbstractModule): Boolean {
val supers = eventClass.supers
for (aSuper in supers) {
@@ -185,10 +107,4 @@
}
return false
}
-
- companion object {
- private val DUMMY_RESULT =
- // cannot return empty array, but this result won't be used anyway
- arrayOfNulls(1).castNotNull()
- }
+}
-}
Index: src/main/kotlin/insight/generation/MethodRenderer.kt
===================================================================
--- src/main/kotlin/insight/generation/MethodRenderer.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/insight/generation/MethodRenderer.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,173 @@
+/*
+ * 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.insight.generation
+
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.actions.AnnotationAttributeRequest
+import com.intellij.lang.jvm.actions.AnnotationAttributeValueRequest
+import com.intellij.psi.PsiType
+import com.intellij.psi.PsiTypes
+
+interface MethodRenderer {
+
+ fun renderMethod(
+ name: String,
+ parameters: List>,
+ modifiers: Set,
+ returnType: PsiType,
+ annotations: List>>
+ ): String
+
+ companion object {
+
+ val byLanguage = mapOf(
+ "JAVA" to JavaRenderer,
+ "kotlin" to KotlinRenderer,
+ )
+ }
+
+ private object JavaRenderer : MethodRenderer {
+
+ override fun renderMethod(
+ name: String,
+ parameters: List>,
+ modifiers: Set,
+ returnType: PsiType,
+ annotations: List>>
+ ): String = buildString {
+ for ((fqn, attributes) in annotations) {
+ renderAnnotation(fqn, attributes)
+ appendLine()
+ }
+
+ when {
+ JvmModifier.PUBLIC in modifiers -> append("public ")
+ JvmModifier.PRIVATE in modifiers -> append("private ")
+ JvmModifier.PROTECTED in modifiers -> append("protected ")
+ }
+
+ when {
+ JvmModifier.STATIC in modifiers -> append("static ")
+ JvmModifier.ABSTRACT in modifiers -> append("abstract ")
+ JvmModifier.FINAL in modifiers -> append("final ")
+ }
+
+ append(returnType.canonicalText)
+ append(' ')
+ append(name)
+ parameters.joinTo(this, prefix = "(", postfix = ")") { (paramName, paramType) ->
+ paramType.canonicalText + " " + paramName
+ }
+ appendLine("{}")
+ }
+
+ fun StringBuilder.renderAnnotation(fqn: String, attributes: List) {
+ append('@')
+ append(fqn)
+ if (attributes.isNotEmpty()) {
+ attributes.joinTo(this, prefix = "(", postfix = ")") { attribute ->
+ attribute.name + " = " + renderAnnotationValue(attribute.value)
+ }
+ }
+ }
+
+ fun renderAnnotationValue(value: AnnotationAttributeValueRequest): String = when (value) {
+ is AnnotationAttributeValueRequest.PrimitiveValue -> value.value.toString()
+ is AnnotationAttributeValueRequest.StringValue -> '"' + value.value + '"'
+ is AnnotationAttributeValueRequest.ClassValue -> value.classFqn + ".class"
+ is AnnotationAttributeValueRequest.ConstantValue -> value.text
+ is AnnotationAttributeValueRequest.NestedAnnotation -> buildString {
+ renderAnnotation(value.annotationRequest.qualifiedName, value.annotationRequest.attributes)
+ }
+
+ is AnnotationAttributeValueRequest.ArrayValue ->
+ value.members.joinToString(prefix = "{", postfix = "}", transform = ::renderAnnotationValue)
+ }
+ }
+
+ private object KotlinRenderer : MethodRenderer {
+
+ override fun renderMethod(
+ name: String,
+ parameters: List>,
+ modifiers: Set,
+ returnType: PsiType,
+ annotations: List>>
+ ): String = buildString {
+ for ((fqn, attributes) in annotations) {
+ renderAnnotation(fqn, attributes)
+ appendLine()
+ }
+
+ if (JvmModifier.STATIC in modifiers) {
+ appendLine("@JvmStatic")
+ }
+
+ when {
+ // Skipping public as it is the default visibility
+ JvmModifier.PRIVATE in modifiers -> append("private ")
+ JvmModifier.PROTECTED in modifiers -> append("protected ")
+ JvmModifier.PACKAGE_LOCAL in modifiers -> append("internal ") // Close enough
+ }
+
+ when {
+ JvmModifier.ABSTRACT in modifiers -> append("abstract ")
+ JvmModifier.FINAL in modifiers -> append("final ")
+ }
+
+ append("fun ")
+ append(name)
+ parameters.joinTo(this, prefix = "(", postfix = ")") { (paramName, paramType) ->
+ paramName + ": " + paramType.canonicalText
+ }
+
+ if (returnType != PsiTypes.voidType()) {
+ append(": ")
+ append(returnType.canonicalText)
+ }
+
+ appendLine("{}")
+ }
+
+ fun StringBuilder.renderAnnotation(fqn: String, attributes: List) {
+ append('@')
+ append(fqn)
+ if (attributes.isNotEmpty()) {
+ attributes.joinTo(this, prefix = "(", postfix = ")") { attribute ->
+ attribute.name + " = " + renderAnnotationValue(attribute.value)
+ }
+ }
+ }
+
+ fun renderAnnotationValue(value: AnnotationAttributeValueRequest): String = when (value) {
+ is AnnotationAttributeValueRequest.PrimitiveValue -> value.value.toString()
+ is AnnotationAttributeValueRequest.StringValue -> '"' + value.value + '"'
+ is AnnotationAttributeValueRequest.ClassValue -> value.classFqn + "::class"
+ is AnnotationAttributeValueRequest.ConstantValue -> value.text
+ is AnnotationAttributeValueRequest.NestedAnnotation -> buildString {
+ renderAnnotation(value.annotationRequest.qualifiedName, value.annotationRequest.attributes)
+ }
+
+ is AnnotationAttributeValueRequest.ArrayValue ->
+ value.members.joinToString(prefix = "[", postfix = "]", transform = ::renderAnnotationValue)
+ }
+ }
+}
Index: src/main/kotlin/insight/generation/MethodRendererBasedEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/insight/generation/MethodRendererBasedEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/insight/generation/MethodRendererBasedEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,94 @@
+/*
+ * 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.insight.generation
+
+import com.intellij.openapi.application.runWriteAction
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiElement
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UDeclaration
+import org.jetbrains.uast.getUastParentOfType
+
+abstract class MethodRendererBasedEventListenerGenerationSupport : EventListenerGenerationSupport {
+
+ override fun canGenerate(context: PsiElement, editor: Editor): Boolean {
+ if (context.language.id !in MethodRenderer.byLanguage) {
+ return false
+ }
+
+ return adjustOffset(context, editor) != null
+ }
+
+ override fun generateEventListener(
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ) = runWriteAction {
+ val document = editor.document
+
+ preGenerationProcess(context, data)
+ PsiDocumentManager.getInstance(context.project).doPostponedOperationsAndUnblockDocument(document)
+
+ val renderer = MethodRenderer.byLanguage[context.language.id]!!
+ val offset = adjustOffset(context, editor) ?: return@runWriteAction
+ val text = invokeRenderer(renderer, context, listenerName, eventClass, data, editor)
+
+ document.insertString(offset, text)
+ PsiDocumentManager.getInstance(context.project).commitDocument(document)
+
+ val file = context.containingFile
+ editor.caretModel.moveToOffset(offset + text.length - 2)
+
+ EventGenHelper.COLLECTOR.forLanguage(file.language)
+ .reformatAndShortenRefs(file, offset, offset + text.length)
+ }
+
+ private fun adjustOffset(context: PsiElement, editor: Editor): Int? {
+ val declaration = context.getUastParentOfType()
+ if (declaration == null) {
+ return null
+ }
+
+ if (declaration is UClass) {
+ return editor.caretModel.offset
+ }
+
+ return declaration.sourcePsi?.textRange?.endOffset
+ }
+
+ protected open fun preGenerationProcess(
+ context: PsiElement,
+ data: GenerationData?,
+ ) = Unit
+
+ protected abstract fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String
+}
Index: src/main/kotlin/insight/generation/ui/EventGenerationPanel.kt
===================================================================
--- src/main/kotlin/insight/generation/ui/EventGenerationPanel.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/insight/generation/ui/EventGenerationPanel.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -43,12 +43,10 @@
/**
* This is called when the dialog is closing from the OK action. The platform should fill in their [GenerationData] object as
* needed for whatever information their panel provides. The state of the panel can be assumed to be valid, since this will only be
- * called if [.doValidate] has passed successfully.
+ * called if [doValidate] has passed successfully.
* @return The [GenerationData] object which will be passed to the
- * * [AbstractModule#doPreEventGenerate()][com.demonwav.mcdev.platform.AbstractModule.doPreEventGenerate] and
- * * [AbstractModule#generateEventListenerMethod][com.demonwav.mcdev.platform.AbstractModule.generateEventListenerMethod]
- * * methods.
+ * [EventListenerGenerationSupport][com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport]
*/
open fun gatherData(): GenerationData? {
return null
Index: src/main/kotlin/platform/AbstractModule.kt
===================================================================
--- src/main/kotlin/platform/AbstractModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/AbstractModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -21,7 +21,7 @@
package com.demonwav.mcdev.platform
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.inspection.IsCancelled
import com.intellij.openapi.module.Module
import com.intellij.psi.PsiClass
@@ -44,6 +44,8 @@
open val icon: Icon?
get() = moduleType.icon
+ open val eventListenerGenSupport: EventListenerGenerationSupport? = null
+
/**
* By default, this method is provided in the case that a specific platform has no
* listener handling whatsoever, or simply accepts event listeners with random
@@ -63,15 +65,6 @@
open fun writeErrorMessageForEventParameter(eventClass: PsiClass, method: PsiMethod) =
"Parameter does not extend the proper Event Class!"
- open fun doPreEventGenerate(psiClass: PsiClass, data: GenerationData?) {}
-
- open fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? = null
-
open fun shouldShowPluginIcon(element: PsiElement?) = false
open fun checkUselessCancelCheck(expression: PsiMethodCallExpression): IsCancelled? {
Index: src/main/kotlin/platform/bukkit/BukkitEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/bukkit/BukkitEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/bukkit/BukkitEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,81 @@
+/*
+ * 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.bukkit
+
+import com.demonwav.mcdev.insight.generation.EventGenHelper
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.bukkit.generation.BukkitGenerationData
+import com.demonwav.mcdev.platform.bukkit.util.BukkitConstants
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.actions.AnnotationAttributeRequest
+import com.intellij.lang.jvm.actions.constantAttribute
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class BukkitEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun preGenerationProcess(
+ context: PsiElement,
+ data: GenerationData?
+ ) {
+ require(data is BukkitGenerationData)
+
+ EventGenHelper.COLLECTOR.forLanguage(context.language)
+ .addImplements(context, BukkitConstants.LISTENER_CLASS)
+ }
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ require(data is BukkitGenerationData)
+
+ val handlerAttributes = mutableListOf()
+ if (data.eventPriority != "NORMAL") {
+ handlerAttributes.add(
+ constantAttribute("priority", BukkitConstants.EVENT_PRIORITY_CLASS + '.' + data.eventPriority)
+ )
+ }
+
+ if (data.isIgnoreCanceled) {
+ handlerAttributes.add(constantAttribute("ignoreCancelled", "true"))
+ }
+
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(
+ BukkitConstants.HANDLER_ANNOTATION to handlerAttributes
+ )
+ )
+ }
+}
Index: src/main/kotlin/platform/bukkit/BukkitModule.kt
===================================================================
--- src/main/kotlin/platform/bukkit/BukkitModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/bukkit/BukkitModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -21,16 +21,14 @@
package com.demonwav.mcdev.platform.bukkit
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.inspection.IsCancelled
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.AbstractModuleType
import com.demonwav.mcdev.platform.PlatformType
-import com.demonwav.mcdev.platform.bukkit.generation.BukkitGenerationData
import com.demonwav.mcdev.platform.bukkit.util.BukkitConstants
import com.demonwav.mcdev.platform.bukkit.util.PaperConstants
import com.demonwav.mcdev.util.SourceType
-import com.demonwav.mcdev.util.addImplements
import com.demonwav.mcdev.util.createVoidMethodWithParameterType
import com.demonwav.mcdev.util.extendsOrImplements
import com.demonwav.mcdev.util.findContainingMethod
@@ -66,6 +64,8 @@
override val moduleType: T = type
+ override val eventListenerGenSupport: EventListenerGenerationSupport? = BukkitEventListenerGenerationSupport()
+
private val pluginParentClasses = listOf(
BukkitConstants.PLUGIN,
PaperConstants.PLUGIN_BOOTSTRAP,
@@ -81,44 +81,6 @@
"Parameter is not a subclass of org.bukkit.event.Event\n" +
"Compiling and running this listener may result in a runtime exception"
- override fun doPreEventGenerate(psiClass: PsiClass, data: GenerationData?) {
- if (!psiClass.extendsOrImplements(BukkitConstants.LISTENER_CLASS)) {
- psiClass.addImplements(BukkitConstants.LISTENER_CLASS)
- }
- }
-
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val bukkitData = data as BukkitGenerationData
-
- val method = generateBukkitStyleEventListenerMethod(
- chosenClass,
- chosenName,
- project,
- BukkitConstants.HANDLER_ANNOTATION,
- bukkitData.isIgnoreCanceled,
- ) ?: return null
-
- if (bukkitData.eventPriority != "NORMAL") {
- val list = method.modifierList
- val annotation = list.findAnnotation(BukkitConstants.HANDLER_ANNOTATION) ?: return method
-
- val value = JavaPsiFacade.getElementFactory(project)
- .createExpressionFromText(
- BukkitConstants.EVENT_PRIORITY_CLASS + "." + bukkitData.eventPriority,
- annotation,
- )
-
- annotation.setDeclaredAttributeValue("priority", value)
- }
-
- return method
- }
-
override fun checkUselessCancelCheck(expression: PsiMethodCallExpression): IsCancelled? {
val method = expression.findContainingMethod() ?: return null
Index: src/main/kotlin/platform/bungeecord/BungeeCordEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/bungeecord/BungeeCordEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/bungeecord/BungeeCordEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,77 @@
+/*
+ * 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.bungeecord
+
+import com.demonwav.mcdev.insight.generation.EventGenHelper
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.bungeecord.generation.BungeeCordGenerationData
+import com.demonwav.mcdev.platform.bungeecord.util.BungeeCordConstants
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.actions.AnnotationAttributeRequest
+import com.intellij.lang.jvm.actions.constantAttribute
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class BungeeCordEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun preGenerationProcess(
+ context: PsiElement,
+ data: GenerationData?
+ ) {
+ require(data is BungeeCordGenerationData)
+
+ EventGenHelper.COLLECTOR.forLanguage(context.language)
+ .addImplements(context, BungeeCordConstants.LISTENER_CLASS)
+ }
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ require(data is BungeeCordGenerationData)
+
+ val handlerAttributes = mutableListOf()
+ if (data.eventPriority != "NORMAL") {
+ handlerAttributes.add(
+ constantAttribute("priority", BungeeCordConstants.EVENT_PRIORITY_CLASS + '.' + data.eventPriority)
+ )
+ }
+
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(
+ BungeeCordConstants.HANDLER_ANNOTATION to handlerAttributes
+ )
+ )
+ }
+}
Index: src/main/kotlin/platform/bungeecord/BungeeCordModule.kt
===================================================================
--- src/main/kotlin/platform/bungeecord/BungeeCordModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/bungeecord/BungeeCordModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -21,19 +21,15 @@
package com.demonwav.mcdev.platform.bungeecord
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.AbstractModuleType
import com.demonwav.mcdev.platform.PlatformType
-import com.demonwav.mcdev.platform.bukkit.BukkitModule
import com.demonwav.mcdev.platform.bukkit.BukkitModuleType
import com.demonwav.mcdev.platform.bukkit.PaperModuleType
import com.demonwav.mcdev.platform.bukkit.SpigotModuleType
-import com.demonwav.mcdev.platform.bungeecord.generation.BungeeCordGenerationData
import com.demonwav.mcdev.platform.bungeecord.util.BungeeCordConstants
import com.demonwav.mcdev.util.SourceType
-import com.demonwav.mcdev.util.addImplements
-import com.demonwav.mcdev.util.extendsOrImplements
import com.demonwav.mcdev.util.nullable
import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions
import com.intellij.lang.jvm.JvmModifier
@@ -65,6 +61,8 @@
override val moduleType: T = type
+ override val eventListenerGenSupport: EventListenerGenerationSupport? = BungeeCordEventListenerGenerationSupport()
+
override fun isEventClassValid(eventClass: PsiClass, method: PsiMethod?) =
BungeeCordConstants.EVENT_CLASS == eventClass.qualifiedName
@@ -72,48 +70,6 @@
"Parameter is not a subclass of net.md_5.bungee.api.plugin.Event\n" +
"Compiling and running this listener may result in a runtime exception"
- override fun doPreEventGenerate(psiClass: PsiClass, data: GenerationData?) {
- val bungeeCordListenerClass = BungeeCordConstants.LISTENER_CLASS
-
- if (!psiClass.extendsOrImplements(bungeeCordListenerClass)) {
- psiClass.addImplements(bungeeCordListenerClass)
- }
- }
-
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val method = BukkitModule.generateBukkitStyleEventListenerMethod(
- chosenClass,
- chosenName,
- project,
- BungeeCordConstants.HANDLER_ANNOTATION,
- false,
- ) ?: return null
-
- val generationData = data as BungeeCordGenerationData? ?: return method
-
- val modifierList = method.modifierList
- val annotation = modifierList.findAnnotation(BungeeCordConstants.HANDLER_ANNOTATION) ?: return method
-
- if (generationData.eventPriority == "NORMAL") {
- return method
- }
-
- val value = JavaPsiFacade.getElementFactory(project)
- .createExpressionFromText(
- BungeeCordConstants.EVENT_PRIORITY_CLASS + "." + generationData.eventPriority,
- annotation,
- )
-
- annotation.setDeclaredAttributeValue("priority", value)
-
- return method
- }
-
override fun shouldShowPluginIcon(element: PsiElement?): Boolean {
val identifier = element?.toUElementOfType()
?: return false
Index: src/main/kotlin/platform/forge/ForgeEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/forge/ForgeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/forge/ForgeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -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.platform.forge
+
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.forge.util.ForgeConstants
+import com.demonwav.mcdev.util.extendsOrImplements
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class ForgeEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ val annotationFqn = if (eventClass.extendsOrImplements(ForgeConstants.FML_EVENT)) {
+ ForgeConstants.EVENT_HANDLER_ANNOTATION
+ } else {
+ ForgeConstants.EVENTBUS_SUBSCRIBE_EVENT_ANNOTATION
+ }
+
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(annotationFqn to emptyList())
+ )
+ }
+}
Index: src/main/kotlin/platform/forge/ForgeModule.kt
===================================================================
--- src/main/kotlin/platform/forge/ForgeModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/forge/ForgeModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -22,7 +22,7 @@
import com.demonwav.mcdev.asset.PlatformAssets
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.inspection.IsCancelled
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.PlatformType
@@ -31,8 +31,6 @@
import com.demonwav.mcdev.platform.mcp.McpModuleSettings
import com.demonwav.mcdev.util.SemanticVersion
import com.demonwav.mcdev.util.SourceType
-import com.demonwav.mcdev.util.createVoidMethodWithParameterType
-import com.demonwav.mcdev.util.extendsOrImplements
import com.demonwav.mcdev.util.nullable
import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions
import com.demonwav.mcdev.util.runWriteTaskLater
@@ -62,6 +60,8 @@
override val type = PlatformType.FORGE
override val icon = PlatformAssets.FORGE_ICON
+ override val eventListenerGenSupport: EventListenerGenerationSupport = ForgeEventListenerGenerationSupport()
+
override fun init() {
ApplicationManager.getApplication().executeOnPooledThread {
waitForAllSmart()
@@ -158,32 +158,6 @@
override fun isStaticListenerSupported(method: PsiMethod) = true
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val isFmlEvent = chosenClass.extendsOrImplements(ForgeConstants.FML_EVENT)
-
- val method = createVoidMethodWithParameterType(project, chosenName, chosenClass) ?: return null
- val modifierList = method.modifierList
-
- if (isFmlEvent) {
- modifierList.addAnnotation(ForgeConstants.EVENT_HANDLER_ANNOTATION)
- } else {
- val mcVersion = McpModuleSettings.getInstance(module).state.minecraftVersion
- ?.let { SemanticVersion.parse(it) }
- if (mcVersion != null && mcVersion >= ForgeModuleType.FG3_MC_VERSION) {
- modifierList.addAnnotation(ForgeConstants.EVENTBUS_SUBSCRIBE_EVENT_ANNOTATION)
- } else {
- modifierList.addAnnotation(ForgeConstants.SUBSCRIBE_EVENT_ANNOTATION)
- }
- }
-
- return method
- }
-
override fun shouldShowPluginIcon(element: PsiElement?): Boolean {
val identifier = element?.toUElementOfType()
?: return false
Index: src/main/kotlin/platform/neoforge/NeoForgeEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/neoforge/NeoForgeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/neoforge/NeoForgeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,52 @@
+/*
+ * 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
+
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class NeoForgeEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(NeoForgeConstants.SUBSCRIBE_EVENT to emptyList())
+ )
+ }
+}
Index: src/main/kotlin/platform/neoforge/NeoForgeModule.kt
===================================================================
--- src/main/kotlin/platform/neoforge/NeoForgeModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/neoforge/NeoForgeModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -22,13 +22,12 @@
import com.demonwav.mcdev.asset.PlatformAssets
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.inspection.IsCancelled
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.PlatformType
import com.demonwav.mcdev.platform.neoforge.util.NeoForgeConstants
import com.demonwav.mcdev.util.SourceType
-import com.demonwav.mcdev.util.createVoidMethodWithParameterType
import com.demonwav.mcdev.util.nullable
import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions
import com.demonwav.mcdev.util.runWriteTaskLater
@@ -54,6 +53,8 @@
override val type = PlatformType.NEOFORGE
override val icon = PlatformAssets.NEOFORGE_ICON
+ override val eventListenerGenSupport: EventListenerGenerationSupport = NeoForgeEventListenerGenerationSupport()
+
override fun init() {
ApplicationManager.getApplication().executeOnPooledThread {
waitForAllSmart()
@@ -97,20 +98,6 @@
override fun isStaticListenerSupported(method: PsiMethod) = true
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val method = createVoidMethodWithParameterType(project, chosenName, chosenClass) ?: return null
- val modifierList = method.modifierList
-
- modifierList.addAnnotation(NeoForgeConstants.SUBSCRIBE_EVENT)
-
- return method
- }
-
override fun shouldShowPluginIcon(element: PsiElement?): Boolean {
val identifier = element?.toUElementOfType()
?: return false
Index: src/main/kotlin/platform/sponge/SpongeEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/sponge/SpongeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/sponge/SpongeEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -0,0 +1,78 @@
+/*
+ * 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
+
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.sponge.generation.SpongeGenerationData
+import com.demonwav.mcdev.platform.sponge.util.SpongeConstants
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.actions.AnnotationAttributeRequest
+import com.intellij.lang.jvm.actions.constantAttribute
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class SpongeEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ require(data is SpongeGenerationData)
+
+ val handlerAnnotations = mutableListOf>>()
+
+ val handlerAttributes = mutableListOf()
+ if (data.eventOrder != "DEFAULT") {
+ handlerAttributes.add(
+ constantAttribute("order", SpongeConstants.ORDER + '.' + data.eventOrder)
+ )
+ }
+
+ handlerAnnotations.add(SpongeConstants.LISTENER_ANNOTATION to handlerAttributes)
+
+ if (!data.isIgnoreCanceled) {
+ handlerAnnotations.add(
+ SpongeConstants.IS_CANCELLED_ANNOTATION to listOf(
+ constantAttribute("value", "org.spongepowered.api.util.Tristate.UNDEFINED")
+ )
+ )
+ }
+
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(
+ SpongeConstants.LISTENER_ANNOTATION to handlerAttributes
+ )
+ )
+ }
+}
Index: src/main/kotlin/platform/sponge/SpongeModule.kt
===================================================================
--- src/main/kotlin/platform/sponge/SpongeModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/sponge/SpongeModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -22,19 +22,16 @@
import com.demonwav.mcdev.asset.PlatformAssets
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.inspection.IsCancelled
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.PlatformType
-import com.demonwav.mcdev.platform.sponge.generation.SpongeGenerationData
import com.demonwav.mcdev.platform.sponge.util.SpongeConstants
-import com.demonwav.mcdev.util.createVoidMethodWithParameterType
import com.demonwav.mcdev.util.extendsOrImplements
import com.demonwav.mcdev.util.findContainingMethod
import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions
import com.intellij.lang.jvm.JvmModifier
import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiAnnotationMemberValue
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
@@ -49,6 +46,8 @@
override val type = PlatformType.SPONGE
override val icon = PlatformAssets.SPONGE_ICON
+ override val eventListenerGenSupport: EventListenerGenerationSupport = SpongeEventListenerGenerationSupport()
+
override fun isEventClassValid(eventClass: PsiClass, method: PsiMethod?) =
"org.spongepowered.api.event.Event" == eventClass.qualifiedName
@@ -56,40 +55,6 @@
"Parameter is not an instance of org.spongepowered.api.event.Event\n" +
"Compiling and running this listener may result in a runtime exception"
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val method = createVoidMethodWithParameterType(project, chosenName, chosenClass) ?: return null
- val modifierList = method.modifierList
-
- val listenerAnnotation = modifierList.addAnnotation("org.spongepowered.api.event.Listener")
-
- val generationData = (data as SpongeGenerationData?)!!
-
- if (!generationData.isIgnoreCanceled) {
- val annotation = modifierList.addAnnotation("org.spongepowered.api.event.filter.IsCancelled")
- val value = JavaPsiFacade.getElementFactory(project)
- .createExpressionFromText("org.spongepowered.api.util.Tristate.UNDEFINED", annotation)
-
- annotation.setDeclaredAttributeValue("value", value)
- }
-
- if (generationData.eventOrder != "DEFAULT") {
- val value = JavaPsiFacade.getElementFactory(project)
- .createExpressionFromText(
- "org.spongepowered.api.event.Order." + generationData.eventOrder,
- listenerAnnotation,
- )
-
- listenerAnnotation.setDeclaredAttributeValue("order", value)
- }
-
- return method
- }
-
override fun shouldShowPluginIcon(element: PsiElement?): Boolean {
val identifier = element?.toUElementOfType()
?: return false
Index: src/main/kotlin/platform/sponge/util/SpongeConstants.kt
===================================================================
--- src/main/kotlin/platform/sponge/util/SpongeConstants.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/sponge/util/SpongeConstants.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -31,6 +31,7 @@
const val TEXT_COLORS = "org.spongepowered.api.text.format.TextColors"
const val EVENT = "org.spongepowered.api.event.Event"
const val LISTENER_ANNOTATION = "org.spongepowered.api.event.Listener"
+ const val ORDER = "org.spongepowered.api.event.Order"
const val GETTER_ANNOTATION = "org.spongepowered.api.event.filter.Getter"
const val IS_CANCELLED_ANNOTATION = "org.spongepowered.api.event.filter.IsCancelled"
const val CANCELLABLE = "org.spongepowered.api.event.Cancellable"
Index: src/main/kotlin/platform/velocity/VelocityEventListenerGenerationSupport.kt
===================================================================
--- src/main/kotlin/platform/velocity/VelocityEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
+++ src/main/kotlin/platform/velocity/VelocityEventListenerGenerationSupport.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -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.platform.velocity
+
+import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.MethodRenderer
+import com.demonwav.mcdev.insight.generation.MethodRendererBasedEventListenerGenerationSupport
+import com.demonwav.mcdev.platform.velocity.generation.VelocityGenerationData
+import com.demonwav.mcdev.platform.velocity.util.VelocityConstants
+import com.demonwav.mcdev.util.psiType
+import com.intellij.lang.jvm.JvmModifier
+import com.intellij.lang.jvm.actions.AnnotationAttributeRequest
+import com.intellij.lang.jvm.actions.constantAttribute
+import com.intellij.openapi.editor.Editor
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiTypes
+
+class VelocityEventListenerGenerationSupport : MethodRendererBasedEventListenerGenerationSupport() {
+
+ override fun invokeRenderer(
+ renderer: MethodRenderer,
+ context: PsiElement,
+ listenerName: String,
+ eventClass: PsiClass,
+ data: GenerationData?,
+ editor: Editor
+ ): String {
+ require(data is VelocityGenerationData)
+
+ val handlerAttributes = mutableListOf()
+ if (data.eventOrder != "NORMAL") {
+ handlerAttributes.add(
+ constantAttribute("order", VelocityConstants.POST_ORDER + '.' + data.eventOrder)
+ )
+ }
+
+ return renderer.renderMethod(
+ listenerName,
+ listOf("event" to eventClass.psiType),
+ setOf(JvmModifier.PUBLIC),
+ PsiTypes.voidType(),
+ listOf(
+ VelocityConstants.SUBSCRIBE_ANNOTATION to handlerAttributes
+ )
+ )
+ }
+}
Index: src/main/kotlin/platform/velocity/VelocityModule.kt
===================================================================
--- src/main/kotlin/platform/velocity/VelocityModule.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/velocity/VelocityModule.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -22,16 +22,12 @@
import com.demonwav.mcdev.asset.PlatformAssets
import com.demonwav.mcdev.facet.MinecraftFacet
-import com.demonwav.mcdev.insight.generation.GenerationData
+import com.demonwav.mcdev.insight.generation.EventListenerGenerationSupport
import com.demonwav.mcdev.platform.AbstractModule
import com.demonwav.mcdev.platform.PlatformType
-import com.demonwav.mcdev.platform.velocity.generation.VelocityGenerationData
import com.demonwav.mcdev.platform.velocity.util.VelocityConstants
-import com.demonwav.mcdev.platform.velocity.util.VelocityConstants.SUBSCRIBE_ANNOTATION
-import com.demonwav.mcdev.util.createVoidMethodWithParameterType
import com.demonwav.mcdev.util.runCatchingKtIdeaExceptions
import com.intellij.lang.jvm.JvmModifier
-import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
@@ -44,34 +40,10 @@
override val type = PlatformType.VELOCITY
override val icon = PlatformAssets.VELOCITY_ICON
+ override val eventListenerGenSupport: EventListenerGenerationSupport = VelocityEventListenerGenerationSupport()
+
override fun isEventClassValid(eventClass: PsiClass, method: PsiMethod?): Boolean = true
- override fun generateEventListenerMethod(
- containingClass: PsiClass,
- chosenClass: PsiClass,
- chosenName: String,
- data: GenerationData?,
- ): PsiMethod? {
- val method = createVoidMethodWithParameterType(project, chosenName, chosenClass) ?: return null
- val modifierList = method.modifierList
-
- val subscribeAnnotation = modifierList.addAnnotation(SUBSCRIBE_ANNOTATION)
-
- val generationData = data as VelocityGenerationData
-
- if (generationData.eventOrder != "NORMAL") {
- val value = JavaPsiFacade.getElementFactory(project)
- .createExpressionFromText(
- "com.velocitypowered.api.event.PostOrder." + generationData.eventOrder,
- subscribeAnnotation,
- )
-
- subscribeAnnotation.setDeclaredAttributeValue("order", value)
- }
-
- return method
- }
-
override fun shouldShowPluginIcon(element: PsiElement?): Boolean {
val identifier = element?.toUElementOfType()
?: return false
Index: src/main/kotlin/platform/velocity/util/VelocityConstants.kt
===================================================================
--- src/main/kotlin/platform/velocity/util/VelocityConstants.kt (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/kotlin/platform/velocity/util/VelocityConstants.kt (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -26,6 +26,7 @@
const val PLUGIN_ANNOTATION = "com.velocitypowered.api.plugin.Plugin"
const val SUBSCRIBE_ANNOTATION = "com.velocitypowered.api.event.Subscribe"
+ const val POST_ORDER = "com.velocitypowered.api.event.PostOrder"
const val KYORI_TEXT_COLOR = "net.kyori.text.format.TextColor"
val API_2 = SemanticVersion.release(2)
Index: src/main/resources/META-INF/mcdev-kotlin.xml
===================================================================
--- src/main/resources/META-INF/mcdev-kotlin.xml (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/resources/META-INF/mcdev-kotlin.xml (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -21,5 +21,7 @@
+
+
Index: src/main/resources/META-INF/plugin.xml
===================================================================
--- src/main/resources/META-INF/plugin.xml (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/resources/META-INF/plugin.xml (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -92,6 +92,10 @@
+
+
+
+
@@ -199,6 +203,8 @@
+
+
Index: src/main/resources/messages/MinecraftDevelopment.properties
===================================================================
--- src/main/resources/messages/MinecraftDevelopment.properties (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/resources/messages/MinecraftDevelopment.properties (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -159,7 +159,7 @@
facet.reimport.failed.content.no_error=Failed to start project refresh, please refresh your project manually.
facet.reimport.failed.content.with_error=Failed to start project refresh, please refresh your project manually. Cause: {0}
-generate.event_listener.title=Generate Event Listener
+generate.event_listener.title=Event Listener
generate.event_listener.settings=Event Listener Settings
generate.event_listener.event_priority=Event Priority
generate.event_listener.event_order=Event Order
Index: src/main/resources/messages/MinecraftDevelopment_fr.properties
===================================================================
--- src/main/resources/messages/MinecraftDevelopment_fr.properties (revision 03fb693644f3cfc2dd2e0fa46a67e2ab2aca2a94)
+++ src/main/resources/messages/MinecraftDevelopment_fr.properties (revision 7198d5f7c00898e8f561a29725279d1f85bcef27)
@@ -18,5 +18,5 @@
# along with this program. If not, see .
#
-generate.event_listener.title=Générer un Event Listener
+generate.event_listener.title=Event Listener
generate.event_listener.settings=Configuration du Listener