User: joe Date: 04 Jan 24 00:58 Revision: d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf Summary: Add references for @Accessor and @Invoker names TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=8946&personal=false Index: src/main/kotlin/platform/mixin/handlers/InvokerHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/InvokerHandler.kt (revision 2ffce66ee88ba80f6ff5a02cee20ba3b33e8c6aa) +++ src/main/kotlin/platform/mixin/handlers/InvokerHandler.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -53,6 +53,10 @@ override fun resolveTarget(annotation: PsiAnnotation, targetClass: ClassNode): List { val member = annotation.parentOfType() ?: return emptyList() val name = getInvokerTargetName(annotation, member) ?: return emptyList() + if (name == "") { + return emptyList() + } + val constructor = name == "" if (constructor && (member.returnType as? PsiClassType)?.resolve()?.fullQualifiedName?.replace('.', '/') != targetClass.name Index: src/main/kotlin/platform/mixin/handlers/MixinMemberAnnotationHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/MixinMemberAnnotationHandler.kt (revision 2ffce66ee88ba80f6ff5a02cee20ba3b33e8c6aa) +++ src/main/kotlin/platform/mixin/handlers/MixinMemberAnnotationHandler.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -21,10 +21,6 @@ package com.demonwav.mcdev.platform.mixin.handlers import com.demonwav.mcdev.platform.mixin.handlers.injectionPoint.InsnResolutionInfo -import com.demonwav.mcdev.platform.mixin.util.FieldTargetMember -import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember -import com.demonwav.mcdev.platform.mixin.util.findSourceElement -import com.demonwav.mcdev.platform.mixin.util.findSourceField import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiElement import org.objectweb.asm.tree.ClassNode @@ -39,24 +35,8 @@ } override fun resolveForNavigation(annotation: PsiAnnotation, targetClass: ClassNode): List { + val project = annotation.project val targets = resolveTarget(annotation, targetClass) - return targets.mapNotNull { - when (it) { - is FieldTargetMember -> - it.classAndField.field.findSourceField( - it.classAndField.clazz, - annotation.project, - annotation.resolveScope, - canDecompile = true, - ) - is MethodTargetMember -> - it.classAndMethod.method.findSourceElement( - it.classAndMethod.clazz, - annotation.project, - annotation.resolveScope, - canDecompile = true, - ) + return targets.mapNotNull { it.findSourceElement(project, annotation.resolveScope, canDecompile = true) } - } - } + } +} - } -} Index: src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt (revision 2ffce66ee88ba80f6ff5a02cee20ba3b33e8c6aa) +++ src/main/kotlin/platform/mixin/handlers/ShadowHandler.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -28,10 +28,6 @@ import com.demonwav.mcdev.platform.mixin.util.MixinTargetMember import com.demonwav.mcdev.platform.mixin.util.findFieldByName import com.demonwav.mcdev.platform.mixin.util.findMethod -import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceField -import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceMethod -import com.demonwav.mcdev.platform.mixin.util.findSourceElement -import com.demonwav.mcdev.platform.mixin.util.findSourceField import com.demonwav.mcdev.util.MemberReference import com.demonwav.mcdev.util.constantStringValue import com.demonwav.mcdev.util.descriptor @@ -84,39 +80,15 @@ fun findFirstShadowTargetForNavigation(member: PsiMember): SmartPsiElementPointer? { val shadow = member.findAnnotation(SHADOW) ?: return null val shadowTarget = resolveTarget(shadow).firstOrNull() ?: return null - return when (shadowTarget) { - is FieldTargetMember -> shadowTarget.classAndField.field.findSourceField( - shadowTarget.classAndField.clazz, - member.project, - member.resolveScope, - canDecompile = false, - ) - is MethodTargetMember -> shadowTarget.classAndMethod.method.findSourceElement( - shadowTarget.classAndMethod.clazz, - member.project, - member.resolveScope, - canDecompile = false, - ) - }?.createSmartPointer() + return shadowTarget.findSourceElement(member.project, member.resolveScope, canDecompile = false) + ?.createSmartPointer() } fun findFirstShadowTargetForReference(member: PsiMember): SmartPsiElementPointer? { val shadow = member.findAnnotation(SHADOW) ?: return null val shadowTarget = resolveTarget(shadow).firstOrNull() ?: return null - return when (shadowTarget) { - is FieldTargetMember -> shadowTarget.classAndField.field.findOrConstructSourceField( - shadowTarget.classAndField.clazz, - member.project, - member.resolveScope, - canDecompile = false, - ) - is MethodTargetMember -> shadowTarget.classAndMethod.method.findOrConstructSourceMethod( - shadowTarget.classAndMethod.clazz, - member.project, - member.resolveScope, - canDecompile = false, - ) - }.createSmartPointer() + return shadowTarget.findOrConstructSourceMember(member.project, member.resolveScope, canDecompile = false) + .createSmartPointer() } private fun hasAliases(shadow: PsiAnnotation) = shadow.findDeclaredAttributeValue("aliases").isNotEmpty() Index: src/main/kotlin/platform/mixin/reference/AccessorReference.kt =================================================================== --- src/main/kotlin/platform/mixin/reference/AccessorReference.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) +++ src/main/kotlin/platform/mixin/reference/AccessorReference.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -0,0 +1,88 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2023 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.reference + +import com.demonwav.mcdev.platform.mixin.handlers.AccessorHandler +import com.demonwav.mcdev.platform.mixin.util.MixinConstants +import com.demonwav.mcdev.platform.mixin.util.findFieldByName +import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceField +import com.demonwav.mcdev.platform.mixin.util.findSourceField +import com.demonwav.mcdev.platform.mixin.util.mixinTargets +import com.demonwav.mcdev.util.descriptor +import com.demonwav.mcdev.util.findContainingClass +import com.demonwav.mcdev.util.findContainingMethod +import com.demonwav.mcdev.util.insideAnnotationAttribute +import com.demonwav.mcdev.util.reference.PolyReferenceResolver +import com.demonwav.mcdev.util.toResolveResults +import com.demonwav.mcdev.util.toTypedArray +import com.intellij.patterns.ElementPattern +import com.intellij.patterns.PsiJavaPatterns +import com.intellij.patterns.StandardPatterns +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiLiteral +import com.intellij.psi.PsiTypes +import com.intellij.psi.ResolveResult +import com.intellij.psi.util.parentOfType +import com.intellij.util.ArrayUtilRt + +object AccessorReference : PolyReferenceResolver() { + val ELEMENT_PATTERN: ElementPattern = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) + .insideAnnotationAttribute(MixinConstants.Annotations.ACCESSOR) + + override fun resolveReference(context: PsiElement): Array { + val project = context.project + return if (context.findContainingMethod() == null) { + // Handle incomplete code that doesn't have a method yet. Just search for the field by name + val name = (context as? PsiLiteral)?.value as? String ?: return ResolveResult.EMPTY_ARRAY + val mixinClass = context.findContainingClass() ?: return ResolveResult.EMPTY_ARRAY + mixinClass.mixinTargets.asSequence().mapNotNull { + it.findFieldByName(name)?.findSourceField(it, project, context.resolveScope, canDecompile = false) + }.toResolveResults() + } else { + val handler = AccessorHandler.getInstance() ?: return ResolveResult.EMPTY_ARRAY + val annotation = context.parentOfType() ?: return ResolveResult.EMPTY_ARRAY + handler.resolveTarget(annotation).asSequence() + .mapNotNull { it.findSourceElement(project, context.resolveScope, canDecompile = false) } + .toResolveResults() + } + } + + override fun collectVariants(context: PsiElement): Array { + val project = context.project + val mixinClass = context.findContainingClass() ?: return ArrayUtilRt.EMPTY_OBJECT_ARRAY + + val fieldDesc = context.findContainingMethod()?.let { method -> + val type = method.returnType ?: return ArrayUtilRt.EMPTY_OBJECT_ARRAY + if (type == PsiTypes.voidType()) { + method.parameterList.getParameter(0)?.type + } else { + type + } + }?.descriptor + + return mixinClass.mixinTargets.asSequence().flatMap { clazz -> + clazz.fields?.filter { field -> fieldDesc == null || field.desc == fieldDesc }?.map { field -> + field.findOrConstructSourceField(clazz, project, context.resolveScope, canDecompile = false) + } ?: emptyList() + }.toTypedArray() + } +} Index: src/main/kotlin/platform/mixin/reference/InvokerReference.kt =================================================================== --- src/main/kotlin/platform/mixin/reference/InvokerReference.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) +++ src/main/kotlin/platform/mixin/reference/InvokerReference.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -0,0 +1,109 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2023 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.reference + +import com.demonwav.mcdev.platform.mixin.handlers.InvokerHandler +import com.demonwav.mcdev.platform.mixin.util.MixinConstants +import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceMethod +import com.demonwav.mcdev.platform.mixin.util.findSourceElement +import com.demonwav.mcdev.platform.mixin.util.isConstructor +import com.demonwav.mcdev.platform.mixin.util.mixinTargets +import com.demonwav.mcdev.util.descriptor +import com.demonwav.mcdev.util.findContainingClass +import com.demonwav.mcdev.util.findContainingMethod +import com.demonwav.mcdev.util.insideAnnotationAttribute +import com.demonwav.mcdev.util.reference.PolyReferenceResolver +import com.demonwav.mcdev.util.toResolveResults +import com.demonwav.mcdev.util.toTypedArray +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.patterns.ElementPattern +import com.intellij.patterns.PsiJavaPatterns +import com.intellij.patterns.StandardPatterns +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiLiteral +import com.intellij.psi.ResolveResult +import com.intellij.psi.util.parentOfType +import com.intellij.util.ArrayUtilRt +import org.objectweb.asm.Type + +object InvokerReference : PolyReferenceResolver() { + val ELEMENT_PATTERN: ElementPattern = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) + .insideAnnotationAttribute(MixinConstants.Annotations.INVOKER) + + override fun resolveReference(context: PsiElement): Array { + val project = context.project + return if (context.findContainingMethod() == null) { + // Handle incomplete code that doesn't have a method yet. Just search for the method by name + val name = (context as? PsiLiteral)?.value as? String ?: return ResolveResult.EMPTY_ARRAY + if (name == "") { + return ResolveResult.EMPTY_ARRAY + } + val mixinClass = context.findContainingClass() ?: return ResolveResult.EMPTY_ARRAY + mixinClass.mixinTargets.asSequence().flatMap { clazz -> + clazz.methods?.filter { method -> method.name == name }?.mapNotNull { method -> + method.findSourceElement(clazz, project, context.resolveScope, canDecompile = false) + } ?: emptyList() + }.toResolveResults() + } else { + val handler = InvokerHandler.getInstance() ?: return ResolveResult.EMPTY_ARRAY + val annotation = context.parentOfType() ?: return ResolveResult.EMPTY_ARRAY + handler.resolveTarget(annotation).asSequence() + .mapNotNull { it.findSourceElement(project, context.resolveScope, canDecompile = false) } + .toResolveResults() + } + } + + override fun collectVariants(context: PsiElement): Array { + val project = context.project + val mixinClass = context.findContainingClass() ?: return ArrayUtilRt.EMPTY_OBJECT_ARRAY + val method = context.findContainingMethod() + if (method == null) { + // Handle incomplete code by returning all methods + return mixinClass.mixinTargets.asSequence().flatMap { clazz -> + clazz.methods?.filter { method -> method.name != "" }?.map { method -> + LookupElementBuilder.create( + method.findOrConstructSourceMethod(clazz, project, context.resolveScope, canDecompile = false), + method.name + ) + } ?: emptyList() + }.toTypedArray() + } else { + val methodDesc = method.descriptor + val constructorType = Type.getReturnType(methodDesc).takeIf { it.sort == Type.OBJECT }?.internalName + val constructorDesc = constructorType?.let { methodDesc?.substringBeforeLast(')') + ")V" } + return mixinClass.mixinTargets.asSequence().flatMap { clazz -> + clazz.methods?.filter { method -> + method.name != "" && if (method.isConstructor) { + clazz.name == constructorType && method.desc == constructorDesc + } else { + method.desc == methodDesc + } + }?.map { method -> + LookupElementBuilder.create( + method.findOrConstructSourceMethod(clazz, project, context.resolveScope, canDecompile = false), + method.name + ) + } ?: emptyList() + }.toTypedArray() + } + } +} Index: src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt =================================================================== --- src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt (revision 2ffce66ee88ba80f6ff5a02cee20ba3b33e8c6aa) +++ src/main/kotlin/platform/mixin/reference/MixinReferenceContributor.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -55,5 +55,15 @@ TargetReference.ELEMENT_PATTERN, TargetReference, ) + + // Accessor references + registrar.registerReferenceProvider( + AccessorReference.ELEMENT_PATTERN, + AccessorReference, + ) + registrar.registerReferenceProvider( + InvokerReference.ELEMENT_PATTERN, + InvokerReference, + ) } } Index: src/main/kotlin/platform/mixin/util/TargetClass.kt =================================================================== --- src/main/kotlin/platform/mixin/util/TargetClass.kt (revision 2ffce66ee88ba80f6ff5a02cee20ba3b33e8c6aa) +++ src/main/kotlin/platform/mixin/util/TargetClass.kt (revision d3ffbd26a76c9b738ad8a8ec0019db5cf2660ebf) @@ -25,8 +25,11 @@ import com.demonwav.mcdev.util.findAnnotation import com.demonwav.mcdev.util.findMethods import com.demonwav.mcdev.util.resolveClass +import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement import com.intellij.psi.PsiMember +import com.intellij.psi.search.GlobalSearchScope import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode @@ -117,18 +120,48 @@ sealed class MixinTargetMember(val mixin: PsiClass?) { abstract val access: Int + + abstract fun findSourceElement( + project: Project, + scope: GlobalSearchScope, + canDecompile: Boolean = false + ): PsiElement? + + abstract fun findOrConstructSourceMember( + project: Project, + scope: GlobalSearchScope, + canDecompile: Boolean = false + ): PsiMember } class FieldTargetMember(val classAndField: ClassAndFieldNode, mixin: PsiClass? = null) : MixinTargetMember(mixin) { constructor(clazz: ClassNode, field: FieldNode) : this(ClassAndFieldNode(clazz, field)) override val access = classAndField.field.access + + override fun findSourceElement(project: Project, scope: GlobalSearchScope, canDecompile: Boolean) = + classAndField.field.findSourceField(classAndField.clazz, project, scope, canDecompile) + + override fun findOrConstructSourceMember( + project: Project, + scope: GlobalSearchScope, + canDecompile: Boolean + ) = classAndField.field.findOrConstructSourceField(classAndField.clazz, project, scope, canDecompile) } class MethodTargetMember(val classAndMethod: ClassAndMethodNode, mixin: PsiClass? = null) : MixinTargetMember(mixin) { constructor(clazz: ClassNode, method: MethodNode) : this(ClassAndMethodNode(clazz, method)) override val access = classAndMethod.method.access + + override fun findSourceElement(project: Project, scope: GlobalSearchScope, canDecompile: Boolean) = + classAndMethod.method.findSourceElement(classAndMethod.clazz, project, scope, canDecompile) + + override fun findOrConstructSourceMember( + project: Project, + scope: GlobalSearchScope, + canDecompile: Boolean + ) = classAndMethod.method.findOrConstructSourceMethod(classAndMethod.clazz, project, scope, canDecompile) } private fun Sequence.filterAccessible(