Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.demonwav.mcdev.platform.bukkit.completion

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<CompletionParameters>() {
companion object {
const val EVENT_LISTENER = "org.bukkit.event.Listener"
private const val BUKKIT_EVENT_FQN = "org.bukkit.event.Event"
}

override fun addCompletions(
@NotNull completionParameters: CompletionParameters,
@NotNull processingContext: ProcessingContext,
@NotNull completionResultSet: CompletionResultSet
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
) {
val prefix = completionResultSet.prefixMatcher.prefix
if (!prefix.startsWith("on") || prefix.length == 2) return
Comment thread
Leguan16 marked this conversation as resolved.
Outdated

val position = completionParameters.position
val containingClass = PsiTreeUtil.getParentOfType(position, PsiClass::class.java) ?: return
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
val project: Project = position.project
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
val facade = JavaPsiFacadeEx.getInstanceEx(project)
Comment thread
Leguan16 marked this conversation as resolved.
Outdated

val eventListenerClass = facade.findClass(EVENT_LISTENER, GlobalSearchScope.allScope(project)) ?: return
if (!containingClass.isInheritor(eventListenerClass, true)) return
if (PsiTreeUtil.getParentOfType(position, PsiMethod::class.java) != null) return
Comment thread
Leguan16 marked this conversation as resolved.
Outdated

val scope = GlobalSearchScope.allScope(project)
val eventBaseClass = facade.findClass(BUKKIT_EVENT_FQN, scope) ?: return

val eventNameFilter = prefix.substring(2).lowercase()

ClassInheritorsSearch.search(eventBaseClass, scope, true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks expensive. However it's not so easy to cache effectively because the easiest way to do that is using the PSI modification count as the cache key, and since you're in a completion provider that is changing on every keystroke. Leave it as it is for now.

.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.replace("Event", "")
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
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()
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.demonwav.mcdev.platform.bukkit.completion

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<LookupElement> {

companion object {
private const val EVENT_HANDLER_FQN = "org.bukkit.event.EventHandler"
}

override fun handleInsert(@NotNull insertionContext: InsertionContext, @NotNull lookupElement: LookupElement) {
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
val project = insertionContext.project
val editor = insertionContext.editor

val templateManager = TemplateManager.getInstance(project)
val template = templateManager.createTemplate("", "")
template.isToReformat = true

template.addTextSegment("@${EVENT_HANDLER_FQN}\n")
template.addTextSegment("public void ")
Comment thread
Leguan16 marked this conversation as resolved.
Outdated
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.getDocument().deleteString(
insertionContext.getStartOffset(),
insertionContext.getTailOffset()
);
Comment thread
Leguan16 marked this conversation as resolved.
Outdated

templateManager.startTemplate(editor, template)

}
}
7 changes: 7 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,13 @@
<library.presentationProvider
implementation="com.demonwav.mcdev.platform.bukkit.framework.ModernPaperPresentationProvider"/>

<!-- Bukkit Completion Contributor -->

<completion.contributor
language="JAVA"
implementationClass="com.demonwav.mcdev.platform.bukkit.completion.BukkitEventHandlerCompletionContributor"
order="first"/>

<!--endregion-->

<!--region SPONGE-->
Expand Down
Loading