User: kyle wood
Date: 05 Aug 26 07:15
Revision: 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf
Summary:
Implement reference provider and auto-complete for @Mixin(target)
TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10597&personal=false
Index: src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt
===================================================================
--- src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt (revision 6da60db01112200c2b4c73795bdf18db17aa2023)
+++ src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
@@ -20,26 +20,34 @@
package com.demonwav.mcdev.platform.mixin.completion
+import com.demonwav.mcdev.platform.mixin.reference.MixinReferences
import com.demonwav.mcdev.platform.mixin.util.findShadowTargets
import com.demonwav.mcdev.platform.mixin.util.isMixin
+import com.demonwav.mcdev.util.constantStringValue
import com.demonwav.mcdev.util.equivalentTo
import com.demonwav.mcdev.util.filter
import com.demonwav.mcdev.util.findContainingClass
+import com.intellij.codeInsight.completion.AllClassesGetter
import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.CompletionType
+import com.intellij.codeInsight.completion.CompletionUtil
import com.intellij.codeInsight.completion.JavaCompletionContributor
import com.intellij.codeInsight.completion.JavaCompletionSorting
+import com.intellij.codeInsight.completion.JavaLookupElementBuilder
import com.intellij.codeInsight.completion.LegacyCompletionContributor
+import com.intellij.codeInsight.completion.PrioritizedLookupElement
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiJavaReference
+import com.intellij.psi.PsiLiteral
import com.intellij.psi.PsiQualifiedReference
import com.intellij.psi.PsiSuperExpression
import com.intellij.psi.PsiThisExpression
+import com.intellij.psi.util.PsiTreeUtil
class MixinCompletionContributor : CompletionContributor() {
@@ -53,6 +61,12 @@
return
}
+ val literal = PsiTreeUtil.getParentOfType(position, PsiLiteral::class.java)
+ if (literal != null && MixinReferences.MIXIN_TARGETS.accepts(literal)) {
+ provideMixinTargetsCompletion(parameters, result, literal)
+ return
+ }
+
// Check if completing inside Mixin class
val psiClass = position.findContainingClass() ?: return
if (!psiClass.isMixin) {
@@ -123,4 +137,35 @@
r.addAllElements(elements)
}
}
+
+ private fun provideMixinTargetsCompletion(
+ parameters: CompletionParameters,
+ result: CompletionResultSet,
+ literal: PsiLiteral,
+ ) {
+ val dummy = CompletionUtil.DUMMY_IDENTIFIER
+ val dummyTrimmed = CompletionUtil.DUMMY_IDENTIFIER_TRIMMED
+ val rawText = literal.constantStringValue ?: ""
+ val text = rawText.removeSuffix(dummy).removeSuffix(dummyTrimmed)
+
+ // If completing a bare class name (no package dot), run global class search
+ if (!text.contains('.')) {
+ AllClassesGetter.processJavaClasses(
+ parameters,
+ result.withPrefixMatcher(text).prefixMatcher,
+ true,
+ ) { cls ->
+ val fqn = cls.qualifiedName ?: return@processJavaClasses
+ val simpleName = cls.name ?: return@processJavaClasses
+ result.addElement(
+ PrioritizedLookupElement.withPriority(
+ JavaLookupElementBuilder.forClass(cls, fqn, true)
+ .withLookupString(fqn)
+ .withLookupString(simpleName),
+ 0.5,
+ ),
+ )
-}
+ }
+ }
+ }
+}
Index: src/main/kotlin/platform/mixin/completion/MixinTargetsTypedHandlerDelegate.kt
===================================================================
--- src/main/kotlin/platform/mixin/completion/MixinTargetsTypedHandlerDelegate.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
+++ src/main/kotlin/platform/mixin/completion/MixinTargetsTypedHandlerDelegate.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
@@ -0,0 +1,45 @@
+/*
+ * 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.mixin.completion
+
+import com.demonwav.mcdev.platform.mixin.reference.MixinReferences
+import com.intellij.codeInsight.AutoPopupController
+import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
+import com.intellij.lang.java.JavaLanguage
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiFile
+import com.intellij.psi.PsiLiteral
+import com.intellij.psi.util.PsiTreeUtil
+
+class MixinTargetsTypedHandlerDelegate : TypedHandlerDelegate() {
+ override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {
+ if ((charTyped == '$' || charTyped == '.') && file.language.isKindOf(JavaLanguage.INSTANCE)) {
+ AutoPopupController.getInstance(project).scheduleAutoPopup(editor) { f ->
+ val offset = editor.caretModel.offset
+ val element = f.findElementAt(offset)
+ val literal = PsiTreeUtil.getParentOfType(element, PsiLiteral::class.java)
+ literal != null && MixinReferences.MIXIN_TARGETS.accepts(literal)
+ }
+ }
+ return Result.CONTINUE
+ }
+}
Index: src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt
===================================================================
--- src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt (revision 6da60db01112200c2b4c73795bdf18db17aa2023)
+++ src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
@@ -23,8 +23,20 @@
import com.demonwav.mcdev.platform.mixin.reference.target.FieldDefinitionReference
import com.demonwav.mcdev.platform.mixin.reference.target.MethodDefinitionReference
import com.demonwav.mcdev.platform.mixin.reference.target.TargetReference
+import com.intellij.codeInsight.AutoPopupController
+import com.intellij.codeInsight.lookup.LookupElement
+import com.intellij.codeInsight.lookup.LookupElementBuilder
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiPackage
+import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceContributor
import com.intellij.psi.PsiReferenceRegistrar
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider
+import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceSet
+import com.intellij.util.PlatformIcons
class MixinReferenceContributor : PsiReferenceContributor() {
@@ -72,5 +84,70 @@
MethodDefinitionReference.ELEMENT_PATTERN,
MethodDefinitionReference,
)
+
+ registrar.registerReferenceProvider(
+ MixinReferences.MIXIN_TARGETS,
+ MixinTargetsClassReferenceProvider(),
+ )
}
}
+
+class MixinTargetsClassReferenceProvider : JavaClassReferenceProvider() {
+ init {
+ setOption(ALLOW_DOLLAR_NAMES, true)
+ setOption(JVM_FORMAT, true)
+ setOption(ADVANCED_RESOLVE, true)
+ setOption(RESOLVE_QUALIFIED_CLASS_NAME, true)
+ }
+
+ override fun getReferencesByString(
+ str: String,
+ position: PsiElement,
+ offsetInPosition: Int,
+ ): Array {
+ return object : JavaClassReferenceSet(str, position, offsetInPosition, true, this) {
+ override fun isAllowDollarInNames(): Boolean = true
+
+ override fun createReference(
+ referenceIndex: Int,
+ referenceText: String,
+ textRange: TextRange,
+ staticImport: Boolean,
+ ): JavaClassReference {
+ return MixinTargetsClassReference(this, referenceIndex, referenceText, textRange, staticImport)
+ }
+ }.allReferences
+ }
+}
+
+class MixinTargetsClassReference(
+ referenceSet: JavaClassReferenceSet,
+ referenceIndex: Int,
+ referenceText: String,
+ textRange: TextRange,
+ staticImport: Boolean,
+) : JavaClassReference(referenceSet, textRange, referenceIndex, referenceText, staticImport) {
+
+ override fun getVariants(): Array {
+ val original = super.getVariants()
+ val list = ArrayList()
+ for (variant in original) {
+ val target = if (variant is LookupElement) variant.`object` else variant
+ if (target is PsiPackage) {
+ val lookupText = if (variant is LookupElement) variant.lookupString else (target.name ?: "")
+ val text = if (lookupText.endsWith(".")) lookupText else "$lookupText."
+ list.add(
+ LookupElementBuilder.create(target, text)
+ .withPresentableText(text)
+ .withIcon(PlatformIcons.PACKAGE_ICON)
+ .withInsertHandler { context, _ ->
+ AutoPopupController.getInstance(context.project).scheduleAutoPopup(context.editor)
+ },
+ )
+ } else if (target is PsiClass) {
+ list.add(variant)
+ }
+ }
+ return list.toArray()
+ }
+}
Index: src/main/kotlin/platform/mixin/reference/MixinReferences.kt
===================================================================
--- src/main/kotlin/platform/mixin/reference/MixinReferences.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
+++ src/main/kotlin/platform/mixin/reference/MixinReferences.kt (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
@@ -0,0 +1,13 @@
+package com.demonwav.mcdev.platform.mixin.reference
+
+import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.MIXIN
+import com.demonwav.mcdev.util.insideAnnotationAttribute
+import com.intellij.patterns.PsiJavaElementPattern
+import com.intellij.patterns.PsiJavaPatterns
+import com.intellij.patterns.StandardPatterns
+import com.intellij.psi.PsiLiteral
+
+object MixinReferences {
+ val MIXIN_TARGETS: PsiJavaElementPattern.Capture = PsiJavaPatterns.psiLiteral(StandardPatterns.string())
+ .insideAnnotationAttribute(MIXIN, "targets")
+}
Index: src/main/resources/META-INF/plugin.xml
===================================================================
--- src/main/resources/META-INF/plugin.xml (revision 6da60db01112200c2b4c73795bdf18db17aa2023)
+++ src/main/resources/META-INF/plugin.xml (revision 23c36a5be58b526e5ae6fe6591cd78de5bceb6cf)
@@ -725,7 +725,7 @@
implementation="com.demonwav.mcdev.platform.mixin.search.MixinSoftImplementMethodSuperSearcher"/>
+ order="first"/>
@@ -747,6 +747,7 @@
implementationClass="com.demonwav.mcdev.platform.mixin.completion.MixinCompletionWeigher"
id="mcdev.mixin" order="first"/>
+