User: noah Date: 02 May 26 16:16 Revision: 632a96f529d7db745696d98348b55e46d38b50b1 Summary: Add completion contributor for Bukkit event handlers (#2613) * Add completion contributor for Bukkit event handlers * cleanup code * add license header * Save template as liveTemplate * add license headers * don't stop processing completions & update year in license header --------- Co-authored-by: Strokkur24 TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10547&personal=false Index: src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt =================================================================== --- src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) +++ src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -0,0 +1,36 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2026 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.completion + +import com.intellij.codeInsight.completion.CompletionContributor +import com.intellij.codeInsight.completion.CompletionType +import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.PsiClass + +class BukkitEventHandlerCompletionContributor : CompletionContributor() { + init { + extend( + CompletionType.BASIC, + PlatformPatterns.psiElement().inside(PsiClass::class.java), + BukkitEventHandlerCompletionProvider() + ) + } +} Index: src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt =================================================================== --- src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) +++ src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -0,0 +1,97 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2026 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.completion + +import com.demonwav.mcdev.platform.bukkit.util.BukkitConstants +import com.demonwav.mcdev.util.findContainingClass +import com.demonwav.mcdev.util.findContainingMethod +import com.intellij.codeInsight.completion.CompletionParameters +import com.intellij.codeInsight.completion.CompletionProvider +import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.codeInsight.completion.PrioritizedLookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.icons.AllIcons +import com.intellij.psi.PsiModifier +import com.intellij.psi.impl.JavaPsiFacadeEx +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.searches.ClassInheritorsSearch +import com.intellij.util.ProcessingContext + +class BukkitEventHandlerCompletionProvider : CompletionProvider() { + + override fun addCompletions( + completionParameters: CompletionParameters, + processingContext: ProcessingContext, + completionResultSet: CompletionResultSet + ) { + val prefix = completionResultSet.prefixMatcher.prefix + if (!prefix.startsWith("on") || prefix.length == 2) { + return + } + + val position = completionParameters.position + val containingClass = position.findContainingClass() ?: return + val project = position.project + val facade = JavaPsiFacadeEx.getInstance(project) + + val eventListenerClass = facade.findClass(BukkitConstants.LISTENER_CLASS, GlobalSearchScope.allScope(project)) ?: return + if (!containingClass.isInheritor(eventListenerClass, true)) { + return + } + + if (position.findContainingMethod() != null) { + return + } + + val scope = GlobalSearchScope.allScope(project) + val eventBaseClass = facade.findClass(BukkitConstants.EVENT_CLASS, scope) ?: return + val eventNameFilter = prefix.substring(2).lowercase() + + ClassInheritorsSearch.search(eventBaseClass, scope, true) + .forEach { psiClass -> + if (psiClass.isInterface || psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + return@forEach + } + + val eventSimpleName = psiClass.name ?: return@forEach + if (eventNameFilter.isNotEmpty() && !eventSimpleName.lowercase().startsWith(eventNameFilter)) { + return@forEach + } + + val lookupString = "on$eventSimpleName" + val qualifiedName = psiClass.qualifiedName + + val element = LookupElementBuilder + .create(lookupString) + .withPresentableText("$lookupString()") + .withTailText(" - $qualifiedName", true) + .withTypeText("@EventHandler") + .withIcon(AllIcons.Nodes.Method) + .withBaseLookupString(lookupString) + .withBoldness(true) + .withInsertHandler(BukkitEventHandlerInsertHandler(psiClass)) + + completionResultSet.addElement( + PrioritizedLookupElement.withPriority(element, 100.0) + ) + } + } +} Index: src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt =================================================================== --- src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) +++ src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -0,0 +1,58 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2026 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.completion + +import com.demonwav.mcdev.util.psiType +import com.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.intention.impl.TypeExpression +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.codeInsight.template.impl.TemplateSettings +import com.intellij.codeInsight.template.impl.Variable +import com.intellij.psi.PsiClass +import com.jetbrains.rd.util.string.printToString + +class BukkitEventHandlerInsertHandler( + private val psiClass: PsiClass +) : InsertHandler { + + override fun handleInsert(insertionContext: InsertionContext, lookupElement: LookupElement) { + insertionContext.document.deleteString( + insertionContext.startOffset, + insertionContext.tailOffset + ) + + val sourceTemplate = TemplateSettings.getInstance().getTemplate("event_handler", "Bukkit") ?: return + val template = sourceTemplate.copy() + + val classTypeExpr = TypeExpression(insertionContext.project, listOf(psiClass.psiType)) + + val index = template.variables.indexOfFirst { it.name == "EVENT_CLASS" } + if (index != -1) { + template.removeVariable(index) + } + + template.addVariable(Variable("EVENT_CLASS", classTypeExpr, classTypeExpr, false, false)); + + TemplateManager.getInstance(insertionContext.project).startTemplate(insertionContext.editor, template) + } +} Index: src/main/kotlin/platform/bukkit/macro/BukkitEventNameMacro.kt =================================================================== --- src/main/kotlin/platform/bukkit/macro/BukkitEventNameMacro.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) +++ src/main/kotlin/platform/bukkit/macro/BukkitEventNameMacro.kt (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -0,0 +1,42 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2026 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.macro + +import com.intellij.codeInsight.template.Expression +import com.intellij.codeInsight.template.ExpressionContext +import com.intellij.codeInsight.template.Result +import com.intellij.codeInsight.template.TextResult +import com.intellij.codeInsight.template.macro.MacroBase + +class BukkitEventNameMacro : MacroBase("bukkitEventName", "Usage: bukkitEventName(ClassType)") { + override fun calculateResult( + params: Array, + context: ExpressionContext?, + quick: Boolean + ): Result? { + val text = params[0] + ?.calculateResult(context) + ?.toString() ?: return null + + val result = text.split("\\.").last().removeSuffix("Event") + return TextResult(result) + } +} Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 66f57a651940ef2588bab9b001edd1c781e690e4) +++ src/main/resources/META-INF/plugin.xml (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -532,6 +532,17 @@ + + + + + + + + Index: src/main/resources/liveTemplates/Bukkit.xml =================================================================== --- src/main/resources/liveTemplates/Bukkit.xml (revision 632a96f529d7db745696d98348b55e46d38b50b1) +++ src/main/resources/liveTemplates/Bukkit.xml (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -0,0 +1,32 @@ + + + + + Index: src/main/resources/messages/MinecraftDevelopment.properties =================================================================== --- src/main/resources/messages/MinecraftDevelopment.properties (revision 66f57a651940ef2588bab9b001edd1c781e690e4) +++ src/main/resources/messages/MinecraftDevelopment.properties (revision 632a96f529d7db745696d98348b55e46d38b50b1) @@ -370,3 +370,5 @@ template.provider.remote.label=Remote template.provider.local.label=Local template.provider.zip.label=Archive + +live.template.bukkit.event_handler.description=Create an event handler