diff --git a/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt new file mode 100644 index 000000000..d7702e672 --- /dev/null +++ b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionContributor.kt @@ -0,0 +1,36 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 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() + ) + } +} diff --git a/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt new file mode 100644 index 000000000..f388bd0f9 --- /dev/null +++ b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerCompletionProvider.kt @@ -0,0 +1,105 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 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.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiMethod +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.psi.util.PsiTreeUtil +import com.intellij.util.ProcessingContext +import org.jetbrains.annotations.NotNull + +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 methodName = lookupString.removeSuffix("Event") + 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(methodName, qualifiedName)) + + completionResultSet.addElement( + PrioritizedLookupElement.withPriority(element, 100.0) + ) + } + + completionResultSet.stopHere() + } +} diff --git a/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt new file mode 100644 index 000000000..653e12ece --- /dev/null +++ b/src/main/kotlin/platform/bukkit/completion/BukkitEventHandlerInsertHandler.kt @@ -0,0 +1,62 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 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.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.codeInsight.template.impl.TextExpression +import com.intellij.openapi.util.NlsSafe +import org.jetbrains.annotations.NotNull + +class BukkitEventHandlerInsertHandler( + private val methodName: String, + private val qualifiedName: @NlsSafe String? +) : InsertHandler { + + override fun handleInsert(insertionContext: InsertionContext, lookupElement: LookupElement) { + val project = insertionContext.project + val editor = insertionContext.editor + + val templateManager = TemplateManager.getInstance(project) + val template = templateManager.createTemplate("", "") + template.isToReformat = true + + template.addTextSegment("@${BukkitConstants.HANDLER_ANNOTATION}\n") + template.addTextSegment("public void ") + template.addVariable("METHOD_NAME", TextExpression(methodName), true) + template.addTextSegment("($qualifiedName ") + template.addVariable("EVENT_PARAM", TextExpression("event"), true) + template.addTextSegment(") { \n") + template.addEndVariable() + template.addTextSegment("\n}") + + insertionContext.document.deleteString( + insertionContext.startOffset, + insertionContext.tailOffset + ) + + templateManager.startTemplate(editor, template) + + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 05cbc4dc0..13ca850e5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -532,6 +532,13 @@ + + + +