User: bawnorton Date: 10 Nov 26 00:32 Revision: 533678d90250de5d335d0d7478938b9a27b2b056 Summary: Mixin shadow target resolution extension (#2657) * Allow extensions to resolve additional shadow targets * Add completion contribution support to shadow target resolver extension TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10689&personal=false Index: src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt =================================================================== --- src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt (revision 6b1657e05af4782c7ebf9f0dd2d55bea91d67972) +++ src/main/kotlin/platform/mixin/completion/MixinCompletionContributor.kt (revision 533678d90250de5d335d0d7478938b9a27b2b056) @@ -21,6 +21,7 @@ package com.demonwav.mcdev.platform.mixin.completion import com.demonwav.mcdev.platform.mixin.reference.MixinReferences +import com.demonwav.mcdev.platform.mixin.handlers.ShadowTargetResolver import com.demonwav.mcdev.platform.mixin.util.findShadowTargets import com.demonwav.mcdev.platform.mixin.util.isMixin import com.demonwav.mcdev.util.constantStringValue @@ -124,7 +125,9 @@ } // Process methods and fields from target class - val elements = findShadowTargets(psiClass, start, superMixin != null) + val extensionTargets = ShadowTargetResolver.EP_NAME.extensionList.asSequence() + .flatMap { it.findCompletionTargets(start) } + val elements = (findShadowTargets(psiClass, start, superMixin != null) + extensionTargets) .filter { val name = it.name StringUtil.isJavaIdentifier(name) && prefixMatcher.prefixMatches(name) Index: src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt (revision 6b1657e05af4782c7ebf9f0dd2d55bea91d67972) +++ src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt (revision 533678d90250de5d335d0d7478938b9a27b2b056) @@ -49,7 +49,7 @@ if (hasAliases(annotation)) return emptyList() val member = annotation.parentOfType() ?: return emptyList() val name = getEffectiveName(annotation, member) ?: return emptyList() - return when (member) { + val targetMembers = when (member) { is PsiMethod -> listOfNotNull( targetClass.findMethod(MemberReference(name, member.descriptor)) ?.let { MethodTargetMember(targetClass, it) }, @@ -59,7 +59,11 @@ ) else -> emptyList() } + if (targetMembers.isNotEmpty()) return targetMembers + return ShadowTargetResolver.EP_NAME.extensionList.flatMap { + it.resolveTarget(annotation, targetClass, member, name) - } + } + } override fun isUnresolved(annotation: PsiAnnotation, targetClass: ClassNode): InsnResolutionInfo.Failure? { if (hasAliases(annotation)) { Index: src/main/kotlin/platform/mixin/handlers/ShadowTargetResolver.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ShadowTargetResolver.kt (revision 533678d90250de5d335d0d7478938b9a27b2b056) +++ src/main/kotlin/platform/mixin/handlers/ShadowTargetResolver.kt (revision 533678d90250de5d335d0d7478938b9a27b2b056) @@ -0,0 +1,44 @@ +/* + * 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.handlers + +import com.demonwav.mcdev.platform.mixin.util.MixinTargetMember +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiClass +import org.objectweb.asm.tree.ClassNode + +/** Supplies shadow targets contributed by other transformations of the same target class. */ +interface ShadowTargetResolver { + fun resolveTarget( + shadow: PsiAnnotation, + targetClass: ClassNode, + member: PsiMember, + effectiveName: String, + ): List + + fun findCompletionTargets(mixinClass: PsiClass): Sequence = emptySequence() + + companion object { + val EP_NAME = ExtensionPointName.create("com.demonwav.minecraft-dev.shadowTargetResolver") + } +} Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 6b1657e05af4782c7ebf9f0dd2d55bea91d67972) +++ src/main/resources/META-INF/plugin.xml (revision 533678d90250de5d335d0d7478938b9a27b2b056) @@ -111,6 +111,8 @@ +