From d46da18d670b560a3f3cc96699d6513aa10fbb0d Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Wed, 3 Jul 2024 14:46:48 +0100 Subject: [PATCH 01/12] MixinExtras: Fix completion confidence. --- .../platform/mixin/completion/MixinCompletionConfidence.kt | 1 + src/main/kotlin/platform/mixin/util/MixinConstants.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt b/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt index d8d5535a8..d3a5c35f0 100644 --- a/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt +++ b/src/main/kotlin/platform/mixin/completion/MixinCompletionConfidence.kt @@ -39,6 +39,7 @@ class MixinCompletionConfidence : CompletionConfidence() { PsiJavaPatterns.psiAnnotation().qName( StandardPatterns.or( StandardPatterns.string().startsWith(MixinConstants.PACKAGE), + StandardPatterns.string().startsWith(MixinConstants.MixinExtras.PACKAGE), StandardPatterns.string() .oneOf(MixinAnnotationHandler.getBuiltinHandlers().map { it.first }.toList()), ) diff --git a/src/main/kotlin/platform/mixin/util/MixinConstants.kt b/src/main/kotlin/platform/mixin/util/MixinConstants.kt index 67f145214..173fc2050 100644 --- a/src/main/kotlin/platform/mixin/util/MixinConstants.kt +++ b/src/main/kotlin/platform/mixin/util/MixinConstants.kt @@ -84,6 +84,7 @@ object MixinConstants { } object MixinExtras { + const val PACKAGE = "com.llamalad7.mixinextras." const val OPERATION = "com.llamalad7.mixinextras.injector.wrapoperation.Operation" const val WRAP_OPERATION = "com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation" const val WRAP_METHOD = "com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod" From a07aa7b4f3c12ba7e540f68a4eaccff3905aa9aa Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Wed, 3 Jul 2024 18:07:01 +0100 Subject: [PATCH 02/12] Expressions: Autocomplete `method`s and `field`s using flows not instructions. --- .../reference/target/DefinitionReferences.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt index 3c61547b5..f9b6b69c8 100644 --- a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt +++ b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt @@ -20,6 +20,7 @@ package com.demonwav.mcdev.platform.mixin.reference.target +import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler import com.demonwav.mcdev.platform.mixin.reference.MixinReference import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember @@ -46,12 +47,12 @@ import com.intellij.psi.PsiMember import com.intellij.psi.ResolveResult import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.containers.sequenceOfNotNull -import org.objectweb.asm.tree.AbstractInsnNode +import com.llamalad7.mixinextras.expression.impl.flow.FlowValue import org.objectweb.asm.tree.FieldInsnNode import org.objectweb.asm.tree.MethodInsnNode abstract class AbstractDefinitionReference : PolyReferenceResolver(), MixinReference { - abstract fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? + abstract fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? abstract fun getMatchesInClass(memberReference: MemberReference, clazz: PsiClass): Sequence abstract fun referenceToString(memberReference: MemberReference): String @@ -110,8 +111,14 @@ abstract class AbstractDefinitionReference : PolyReferenceResolver(), MixinRefer continue } - for (insn in target.classAndMethod.method.instructions) { - val fullReference = getFullReferenceIfMatches(memberReference, insn) ?: continue + val flow = MEExpressionMatchUtil.getFlowMap( + project, + target.classAndMethod.clazz, + target.classAndMethod.method + ) ?: continue + + for (node in flow.values) { + val fullReference = getFullReferenceIfMatches(memberReference, node) ?: continue result += fullReference } } @@ -124,7 +131,8 @@ object FieldDefinitionReference : AbstractDefinitionReference() { val ELEMENT_PATTERN = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) .insideAnnotationAttribute(MixinConstants.MixinExtras.DEFINITION, "field") - override fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? { + override fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? { + val insn = node.insn if (insn !is FieldInsnNode || !memberReference.matchField(insn.owner, insn.name, insn.desc)) { return null } @@ -145,7 +153,8 @@ object MethodDefinitionReference : AbstractDefinitionReference() { val ELEMENT_PATTERN = PsiJavaPatterns.psiLiteral(StandardPatterns.string()) .insideAnnotationAttribute(MixinConstants.MixinExtras.DEFINITION, "method") - override fun getFullReferenceIfMatches(memberReference: MemberReference, insn: AbstractInsnNode): MemberReference? { + override fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? { + val insn = node.insn if (insn !is MethodInsnNode || !memberReference.matchMethod(insn.owner, insn.name, insn.desc)) { return null } From ed3890eb4c580c4aec57a3786a4eb1e36638847f Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Wed, 3 Jul 2024 18:50:47 +0100 Subject: [PATCH 03/12] Expressions: Autocomplete `method`s for method references. --- build.gradle.kts | 2 +- .../mixin/reference/target/DefinitionReferences.kt | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 99e7431fb..88631624a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,7 +102,7 @@ dependencies { implementation(files(Jvm.current().toolsJar)) // TODO: temporary waiting for a release - fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:4d2e01e" + fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:371c39e" implementation(mixinExtras("expressions")) testLibs(mixinExtras("common")) diff --git a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt index f9b6b69c8..a6927b8a8 100644 --- a/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt +++ b/src/main/kotlin/platform/mixin/reference/target/DefinitionReferences.kt @@ -48,6 +48,8 @@ import com.intellij.psi.ResolveResult import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.containers.sequenceOfNotNull import com.llamalad7.mixinextras.expression.impl.flow.FlowValue +import com.llamalad7.mixinextras.expression.impl.flow.postprocessing.LMFInfo +import com.llamalad7.mixinextras.expression.impl.utils.FlowDecorations import org.objectweb.asm.tree.FieldInsnNode import org.objectweb.asm.tree.MethodInsnNode @@ -154,12 +156,20 @@ object MethodDefinitionReference : AbstractDefinitionReference() { .insideAnnotationAttribute(MixinConstants.MixinExtras.DEFINITION, "method") override fun getFullReferenceIfMatches(memberReference: MemberReference, node: FlowValue): MemberReference? { + val info = node.getDecoration(FlowDecorations.LMF_INFO) val insn = node.insn - if (insn !is MethodInsnNode || !memberReference.matchMethod(insn.owner, insn.name, insn.desc)) { + val (owner, name, desc) = when { + info != null && (info.type == LMFInfo.Type.FREE_METHOD || info.type == LMFInfo.Type.BOUND_METHOD) -> + Triple(info.impl.owner, info.impl.name, info.impl.desc) + + insn is MethodInsnNode -> Triple(insn.owner, insn.name, insn.desc) + else -> return null + } + if (!memberReference.matchMethod(owner, name, desc)) { return null } - return MemberReference(insn.name, insn.desc, insn.owner.replace('/', '.')) + return MemberReference(name, desc, owner.replace('/', '.')) } override fun getMatchesInClass(memberReference: MemberReference, clazz: PsiClass) = From e846ad81ce472b9cf7274bf98eae2605a6a14e14 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 12:30:44 +0100 Subject: [PATCH 04/12] Expressions: A class constant is an expression. --- .../platform/mixin/expression/MEExpressionCompletionUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index dcda90ba2..b3e5d705a 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -695,7 +695,7 @@ object MEExpressionCompletionUtil { is LdcInsnNode -> { when (val cst = insn.insn.cst) { is Type -> { - if (canCompleteTypes && cst.isAccessibleFrom(mixinClass)) { + if (canCompleteExprs && cst.isAccessibleFrom(mixinClass)) { return listOf( createTypeLookup(cst) .withTailText(".class") From 4fb871890d3319d5d9a8fc84933c0a0ce6a7b6b8 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 13:10:40 +0100 Subject: [PATCH 05/12] Expressions: Show instantiation desc in autocomplete. --- .../expression/MEExpressionCompletionUtil.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index b3e5d705a..4e8bd7fe9 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -21,6 +21,7 @@ package com.demonwav.mcdev.platform.mixin.expression import com.demonwav.mcdev.MinecraftProjectSettings +import com.demonwav.mcdev.platform.mixin.expression.MEExpressionCompletionUtil.presentableName import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil.virtualInsn import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil.virtualInsnOrNull import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEArrayAccessExpression @@ -746,9 +747,7 @@ object MEExpressionCompletionUtil { var lookup = LookupElementBuilder.create(insn.insn.name.toValidIdentifier()) .withIcon(PlatformIcons.METHOD_ICON) .withPresentableText(insn.insn.owner.substringAfterLast('/') + "." + insn.insn.name) - .withTailText( - "(" + Type.getArgumentTypes(insn.insn.desc).joinToString { it.presentableName() } + ")" - ) + .withDescTailText(insn.insn.desc) .withTypeText(Type.getReturnType(insn.insn.desc).presentableName()) .withDefinitionAndFold(insn.insn.name.toValidIdentifier(), "method", definitionValue) if (insn.insn.opcode == Opcodes.INVOKESTATIC) { @@ -781,10 +780,13 @@ object MEExpressionCompletionUtil { ?.getDecoration(FlowDecorations.INSTANTIATION_INFO) ?.initCall ?.virtualInsnOrNull - ?.insn as MethodInsnNode? + ?.insn as? MethodInsnNode + ?: return emptyList() return listOf( - lookup.withTail(ParenthesesTailType(initCall?.desc?.startsWith("()") == false)) - .createEliminable("new ${insn.insn.desc}${initCall?.desc}") + lookup + .withDescTailText(initCall.desc) + .withTail(ParenthesesTailType(!initCall.desc.startsWith("()"))) + .createEliminable("new ${insn.insn.desc}${initCall.desc}") ) } else -> return listOf(lookup.createEliminable("type ${insn.insn.desc}")) @@ -1028,6 +1030,11 @@ object MEExpressionCompletionUtil { ) } + private fun LookupElementBuilder.withDescTailText(desc: String) = + withTailText( + Type.getArgumentTypes(desc).joinToString(prefix = "(", postfix = ")") { it.presentableName() } + ) + private fun LookupElement.withTail(tailType: TailType?) = object : TailTypeDecorator(this) { override fun computeTailType(context: InsertionContext?) = tailType } From a9c9a15981ec1457ac3c5e2f81f83497a98e20b8 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 13:26:23 +0100 Subject: [PATCH 06/12] Expressions: Make completions always unique to stop them being filtered. We filter duplicates ourselves. --- .../expression/MEExpressionCompletionUtil.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index 4e8bd7fe9..e58d93b9f 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -728,7 +728,7 @@ object MEExpressionCompletionUtil { is FieldInsnNode -> { if (canCompleteExprs) { val definitionValue = "field = \"L${insn.insn.owner};${insn.insn.name}:${insn.insn.desc}\"" - var lookup = LookupElementBuilder.create(insn.insn.name.toValidIdentifier()) + var lookup = createUniqueLookup(insn.insn.name.toValidIdentifier()) .withIcon(PlatformIcons.FIELD_ICON) .withPresentableText(insn.insn.owner.substringAfterLast('/') + "." + insn.insn.name) .withTypeText(Type.getType(insn.insn.desc).presentableName()) @@ -744,7 +744,7 @@ object MEExpressionCompletionUtil { is MethodInsnNode -> { if (canCompleteExprs) { val definitionValue = "method = \"L${insn.insn.owner};${insn.insn.name}${insn.insn.desc}\"" - var lookup = LookupElementBuilder.create(insn.insn.name.toValidIdentifier()) + var lookup = createUniqueLookup(insn.insn.name.toValidIdentifier()) .withIcon(PlatformIcons.METHOD_ICON) .withPresentableText(insn.insn.owner.substringAfterLast('/') + "." + insn.insn.name) .withDescTailText(insn.insn.desc) @@ -808,7 +808,7 @@ object MEExpressionCompletionUtil { else -> "unknown" // wtf? } return listOf( - LookupElementBuilder.create(type) + createUniqueLookup(type) .withIcon(PlatformIcons.CLASS_ICON) .withTail( BracketsTailType( @@ -841,7 +841,7 @@ object MEExpressionCompletionUtil { Opcodes.ARRAYLENGTH -> { if (canCompleteExprs) { return listOf( - LookupElementBuilder.create("length") + createUniqueLookup("length") .withIcon(PlatformIcons.FIELD_ICON) .withTypeText("int") .createEliminable("arraylength") @@ -870,7 +870,7 @@ object MEExpressionCompletionUtil { ) } else { return listOf( - LookupElementBuilder.create(handle.name.toValidIdentifier()) + createUniqueLookup(handle.name.toValidIdentifier()) .withIcon(PlatformIcons.METHOD_ICON) .withPresentableText(handle.owner.substringAfterLast('/') + "." + handle.name) .withTypeText(Type.getReturnType(handle.desc).presentableName()) @@ -937,7 +937,7 @@ object MEExpressionCompletionUtil { private fun createTypeLookup(type: Type): LookupElementBuilder { val definitionId = type.typeNameToInsert() - val lookupElement = LookupElementBuilder.create(definitionId) + val lookupElement = createUniqueLookup(definitionId) .withIcon(PlatformIcons.CLASS_ICON) .withPresentableText(type.presentableName()) @@ -999,7 +999,7 @@ object MEExpressionCompletionUtil { val ordinal = localsOfMyType.indexOf(localVariable) val isImplicit = localsOfMyType.size == 1 val localName = localVariable.name.toValidIdentifier() - LookupElementBuilder.create(localName) + createUniqueLookup(localName) .withIcon(PlatformIcons.VARIABLE_ICON) .withTypeText(localPsiType.presentableText) .withLocalDefinition( @@ -1022,7 +1022,7 @@ object MEExpressionCompletionUtil { val localName = localType.typeNameToInsert().replace("[]", "Array") + (ordinal + 1) val isImplicit = localTypes.count { it == localType } == 1 return listOf( - LookupElementBuilder.create(localName) + createUniqueLookup(localName) .withIcon(PlatformIcons.VARIABLE_ICON) .withTypeText(localType.presentableName()) .withLocalDefinition(localName, localType, ordinal, isArgsOnly, isImplicit, mixinClass) @@ -1285,6 +1285,8 @@ object MEExpressionCompletionUtil { return variants } + private fun createUniqueLookup(text: String) = LookupElementBuilder.create(Any(), text) + private fun LookupElement.createEliminable(uniquenessKey: String, priority: Int = 0) = EliminableLookup(uniquenessKey, this, priority) From d8f836ebd319078dd277ec461c4a09672ab4d5a9 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 14:27:17 +0100 Subject: [PATCH 07/12] Expressions: Overhaul array completions. Make the preview text more representative of the actual completion and support array literals which were previously missing. --- .../expression/MEExpressionCompletionUtil.kt | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index e58d93b9f..05f9f62e3 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -21,7 +21,6 @@ package com.demonwav.mcdev.platform.mixin.expression import com.demonwav.mcdev.MinecraftProjectSettings -import com.demonwav.mcdev.platform.mixin.expression.MEExpressionCompletionUtil.presentableName import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil.virtualInsn import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil.virtualInsnOrNull import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEArrayAccessExpression @@ -692,6 +691,7 @@ object MEExpressionCompletionUtil { canCompleteExprs: Boolean, canCompleteTypes: Boolean ): List { + val flow = flows[insn] when (insn.insn) { is LdcInsnNode -> { when (val cst = insn.insn.cst) { @@ -765,18 +765,11 @@ object MEExpressionCompletionUtil { val lookup = createTypeLookup(type) when (insn.insn.opcode) { Opcodes.ANEWARRAY -> { - return listOf( - lookup.withTail( - BracketsTailType( - 1, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true, - ) - ) - .createEliminable("new [${insn.insn.desc}") - ) + val arrayType = Type.getType('[' + Type.getObjectType(insn.insn.desc).descriptor) + return createNewArrayCompletion(flow, arrayType) } Opcodes.NEW -> { - val initCall = flows[insn] + val initCall = flow ?.getDecoration(FlowDecorations.INSTANTIATION_INFO) ?.initCall ?.virtualInsnOrNull @@ -796,44 +789,27 @@ object MEExpressionCompletionUtil { is IntInsnNode -> { if (insn.insn.opcode == Opcodes.NEWARRAY) { if (canCompleteTypes) { - val type = when (insn.insn.operand) { - Opcodes.T_BOOLEAN -> "boolean" - Opcodes.T_CHAR -> "char" - Opcodes.T_FLOAT -> "float" - Opcodes.T_DOUBLE -> "double" - Opcodes.T_BYTE -> "byte" - Opcodes.T_SHORT -> "short" - Opcodes.T_INT -> "int" - Opcodes.T_LONG -> "long" - else -> "unknown" // wtf? - } - return listOf( - createUniqueLookup(type) - .withIcon(PlatformIcons.CLASS_ICON) - .withTail( - BracketsTailType( - 1, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true, - ) - ) - .createEliminable("new $type[]") + val arrayType = Type.getType( + when (insn.insn.operand) { + Opcodes.T_BOOLEAN -> "[B" + Opcodes.T_CHAR -> "[C" + Opcodes.T_FLOAT -> "[F" + Opcodes.T_DOUBLE -> "[D" + Opcodes.T_BYTE -> "[B" + Opcodes.T_SHORT -> "[S" + Opcodes.T_INT -> "[I" + Opcodes.T_LONG -> "[J" + else -> "[Lnull;" // wtf? + } ) + return createNewArrayCompletion(flow, arrayType) } } } is MultiANewArrayInsnNode -> { if (canCompleteTypes) { - val type = Type.getType(insn.insn.desc) - return listOf( - createTypeLookup(type.elementType) - .withTail( - BracketsTailType( - type.dimensions, - flows[insn]?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true - ) - ) - .createEliminable("new ${insn.insn.desc}") - ) + val arrayType = Type.getType(insn.insn.desc) + return createNewArrayCompletion(flow, arrayType) } } is InsnNode -> { @@ -948,6 +924,22 @@ object MEExpressionCompletionUtil { } } + private fun createNewArrayCompletion(flow: FlowValue?, arrayType: Type): List { + val hasInitializer = flow?.hasDecoration(FlowDecorations.ARRAY_CREATION_INFO) == true + val initializerText = if (hasInitializer) "{}" else "" + return listOf( + createTypeLookup(arrayType.elementType) + .withTailText("[]".repeat(arrayType.dimensions) + initializerText) + .withTail( + BracketsTailType( + arrayType.dimensions, + hasInitializer, + ) + ) + .createEliminable("new ${arrayType.descriptor}$initializerText") + ) + } + private fun createLocalVariableLookups( project: Project, targetClass: ClassNode, @@ -1278,6 +1270,10 @@ object MEExpressionCompletionUtil { val fixedNewArrayExpr = factory.createExpression("new ?[?]") as MENewExpression fixedNewArrayExpr.type!!.replace(type) variants += fixedNewArrayExpr + + val arrayLitExpr = factory.createExpression("new ?[]{?}") as MENewExpression + arrayLitExpr.type!!.replace(type) + variants += arrayLitExpr } } } From 901696c0760ec5fbc3216015b263c4a59489a2d6 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 14:52:30 +0100 Subject: [PATCH 08/12] Expressions: Fix super call completions. --- .../platform/mixin/expression/MEExpressionCompletionUtil.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index 05f9f62e3..1c2617d10 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -1276,6 +1276,12 @@ object MEExpressionCompletionUtil { variants += arrayLitExpr } } + is MESuperCallExpression -> { + // Might be missing its parentheses + val callExpr = factory.createExpression("super.?()") as MESuperCallExpression + expression.memberName?.let { callExpr.memberName!!.replace(it) } + variants += callExpr + } } return variants From 79df568389214b69c9fee757a0f104ebde89002c Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 15:57:49 +0100 Subject: [PATCH 09/12] Expressions: Confidently suggest completions after `::`. --- .../MEExpressionTypedHandlerDelegate.kt | 42 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + 2 files changed, 43 insertions(+) create mode 100644 src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt new file mode 100644 index 000000000..6d714b43b --- /dev/null +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionTypedHandlerDelegate.kt @@ -0,0 +1,42 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 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.expression + +import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionTypes +import com.intellij.codeInsight.AutoPopupController +import com.intellij.codeInsight.editorActions.TypedHandlerDelegate +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.util.elementType + +class MEExpressionTypedHandlerDelegate : TypedHandlerDelegate() { + override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result { + if (charTyped == ':' && file.language == MEExpressionLanguage) { + AutoPopupController.getInstance(project).autoPopupMemberLookup(editor) { + val offset = editor.caretModel.offset + it.findElementAt(offset - 1).elementType == MEExpressionTypes.TOKEN_METHOD_REF + } + return Result.STOP + } + return Result.CONTINUE + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 307909a6e..273df3975 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -514,6 +514,7 @@ + From aa7241f15cd71bd8884438613921d9c40ed30030 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 17:01:18 +0100 Subject: [PATCH 10/12] Expressions: Add intention action to define unresolved identifiers. --- .../mixin/expression/MEExpressionAnnotator.kt | 49 +++++++++++++++++++ .../expression/MEExpressionCompletionUtil.kt | 15 ++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt index bcabbf930..1c70d2326 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt @@ -40,16 +40,24 @@ import com.demonwav.mcdev.platform.mixin.expression.gen.psi.METype import com.demonwav.mcdev.platform.mixin.expression.psi.METypeUtil import com.demonwav.mcdev.platform.mixin.util.MixinConstants import com.demonwav.mcdev.util.findMultiInjectionHost +import com.intellij.codeInsight.AutoPopupController import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.RemoveAnnotationQuickFix +import com.intellij.lang.annotation.AnnotationBuilder import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.Annotator import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.project.Project import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.util.TypeConversionUtil import com.intellij.psi.util.parentOfType @@ -275,6 +283,7 @@ class MEExpressionAnnotator : Annotator { ) .range(type) .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .withDefinitionFix(type) .create() } else { holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES) @@ -306,6 +315,7 @@ class MEExpressionAnnotator : Annotator { ) .range(variable) .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .withDefinitionFix(variable) .create() } else { holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES) @@ -314,4 +324,43 @@ class MEExpressionAnnotator : Annotator { .create() } } + + private fun AnnotationBuilder.withDefinitionFix(name: MEName) = + withFix(AddDefinitionInspection(name)) + + private class AddDefinitionInspection(name: MEName) : LocalQuickFixAndIntentionActionOnPsiElement(name) { + private val id = name.text + + override fun getFamilyName(): String = "Add @Definition" + + override fun getText(): String = "$familyName(id = \"$id\")" + + override fun invoke( + project: Project, + file: PsiFile, + editor: Editor?, + startElement: PsiElement, + endElement: PsiElement + ) { + if (editor == null) { + MEExpressionCompletionUtil.addDefinition( + startElement, + id, + "" + ) + return + } + val annotation = MEExpressionCompletionUtil.addDefinition( + startElement, + id, + "dummy" + ) ?: return + val dummy = annotation.findAttribute("dummy") as? PsiElement ?: return + val hostEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor) + hostEditor.caretModel.moveToOffset(dummy.textOffset) + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(hostEditor.document) + hostEditor.document.replaceString(dummy.textRange.startOffset, dummy.textRange.endOffset, "") + AutoPopupController.getInstance(project).autoPopupMemberLookup(hostEditor, null) + } + } } diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index 1c2617d10..3801ad83f 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -1178,6 +1178,11 @@ object MEExpressionCompletionUtil { private fun addDefinition(context: InsertionContext, id: String, definitionValue: String): PsiAnnotation? { val contextElement = context.file.findElementAt(context.startOffset) ?: return null + return addDefinition(contextElement, id, definitionValue) + } + + fun addDefinition(contextElement: PsiElement, id: String, definitionValue: String): PsiAnnotation? { + val project = contextElement.project val injectionHost = contextElement.findMultiInjectionHost() ?: return null val expressionAnnotation = injectionHost.parentOfType() ?: return null if (!expressionAnnotation.hasQualifiedName(MixinConstants.MixinExtras.EXPRESSION)) { @@ -1195,14 +1200,14 @@ object MEExpressionCompletionUtil { } // create and add the new @Definition annotation - var newAnnotation = JavaPsiFacade.getElementFactory(context.project).createAnnotationFromText( + var newAnnotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText( "@${MixinConstants.MixinExtras.DEFINITION}(id = \"$id\", $definitionValue)", modifierList, ) var anchor = modifierList.annotations.lastOrNull { it.hasQualifiedName(MixinConstants.MixinExtras.DEFINITION) } if (anchor == null) { val definitionPosRelativeToExpression = - MinecraftProjectSettings.getInstance(context.project).definitionPosRelativeToExpression + MinecraftProjectSettings.getInstance(project).definitionPosRelativeToExpression if (definitionPosRelativeToExpression == BeforeOrAfter.AFTER) { anchor = expressionAnnotation } @@ -1211,11 +1216,11 @@ object MEExpressionCompletionUtil { // add imports and reformat newAnnotation = - JavaCodeStyleManager.getInstance(context.project).shortenClassReferences(newAnnotation) as PsiAnnotation - JavaCodeStyleManager.getInstance(context.project).optimizeImports(modifierList.containingFile) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(newAnnotation) as PsiAnnotation + JavaCodeStyleManager.getInstance(project).optimizeImports(modifierList.containingFile) val annotationIndex = modifierList.annotations.indexOf(newAnnotation) val formattedModifierList = - CodeStyleManager.getInstance(context.project).reformat(modifierList) as PsiModifierList + CodeStyleManager.getInstance(project).reformat(modifierList) as PsiModifierList return formattedModifierList.annotations.getOrNull(annotationIndex) } From 5831faa3731a120dee94314ada71bcd80fcd2d7b Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Thu, 4 Jul 2024 18:49:31 +0100 Subject: [PATCH 11/12] Expressions: Fix `@Local`s not properly handling compound insns. Also adapt to related MixinExtras changes. --- build.gradle.kts | 2 +- .../mixin/expression/MEExpressionMatchUtil.kt | 27 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 88631624a..0e8cf8ae6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -102,7 +102,7 @@ dependencies { implementation(files(Jvm.current().toolsJar)) // TODO: temporary waiting for a release - fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:371c39e" + fun mixinExtras(variant: String) = "com.github.LlamaLad7.MixinExtras:mixinextras-$variant:2ad48e8" implementation(mixinExtras("expressions")) testLibs(mixinExtras("common")) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt index 1461facc4..541cdd455 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt @@ -48,7 +48,7 @@ import com.llamalad7.mixinextras.expression.impl.flow.FlowValue import com.llamalad7.mixinextras.expression.impl.flow.expansion.InsnExpander import com.llamalad7.mixinextras.expression.impl.point.ExpressionContext import com.llamalad7.mixinextras.expression.impl.pool.IdentifierPool -import com.llamalad7.mixinextras.expression.impl.pool.MemberDefinition +import com.llamalad7.mixinextras.expression.impl.pool.SimpleMemberDefinition import org.objectweb.asm.Handle import org.objectweb.asm.Opcodes import org.objectweb.asm.Type @@ -171,9 +171,12 @@ object MEExpressionMatchUtil { val fields = annotation.findDeclaredAttributeValue("field")?.computeStringArray() ?: emptyList() for (field in fields) { val fieldRef = MemberReference.parse(field) ?: continue - pool.addMember(definitionId) { - it is FieldInsnNode && fieldRef.matchField(it.owner, it.name, it.desc) - } + pool.addMember( + definitionId, + SimpleMemberDefinition { + it is FieldInsnNode && fieldRef.matchField(it.owner, it.name, it.desc) + } + ) } val methods = annotation.findDeclaredAttributeValue("method")?.computeStringArray() ?: emptyList() @@ -181,7 +184,7 @@ object MEExpressionMatchUtil { val methodRef = MemberReference.parse(method) ?: continue pool.addMember( definitionId, - object : MemberDefinition { + object : SimpleMemberDefinition { override fun matches(insn: AbstractInsnNode) = insn is MethodInsnNode && methodRef.matchMethod(insn.owner, insn.name, insn.desc) @@ -202,20 +205,22 @@ object MEExpressionMatchUtil { for (localAnnotation in locals) { val localType = localAnnotation.findDeclaredAttributeValue("type")?.resolveType() val localInfo = LocalInfo.fromAnnotation(localType, localAnnotation) - pool.addMember(definitionId) { insn -> - if (insn !is VarInsnNode) { + pool.addMember(definitionId) { node -> + val virtualInsn = node.insn + if (virtualInsn !is VarInsnNode) { return@addMember false } - val actualInsn = if (insn.opcode >= Opcodes.ISTORE && insn.opcode <= Opcodes.ASTORE) { - insn.next ?: return@addMember false + val physicalInsn = InsnExpander.getRepresentative(node) + val actualInsn = if (virtualInsn.opcode >= Opcodes.ISTORE && virtualInsn.opcode <= Opcodes.ASTORE) { + physicalInsn.next ?: return@addMember false } else { - insn + physicalInsn } val unfilteredLocals = localInfo.getLocals(module, targetClass, targetMethod, actualInsn) ?: return@addMember false val filteredLocals = localInfo.matchLocals(unfilteredLocals, CollectVisitor.Mode.MATCH_ALL) - filteredLocals.any { it.index == insn.`var` } + filteredLocals.any { it.index == virtualInsn.`var` } } } } From 6df8d302d2b95f54b6df9edc808ad8a5223d5915 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Tue, 9 Jul 2024 20:07:59 +0100 Subject: [PATCH 12/12] Refactor: Add `project` as parameter to `MEExpressionCompletionUtil.addDefinition` --- .../platform/mixin/expression/MEExpressionAnnotator.kt | 2 ++ .../platform/mixin/expression/MEExpressionCompletionUtil.kt | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt index 1c70d2326..638bf6f13 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt @@ -344,6 +344,7 @@ class MEExpressionAnnotator : Annotator { ) { if (editor == null) { MEExpressionCompletionUtil.addDefinition( + project, startElement, id, "" @@ -351,6 +352,7 @@ class MEExpressionAnnotator : Annotator { return } val annotation = MEExpressionCompletionUtil.addDefinition( + project, startElement, id, "dummy" diff --git a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt index 3801ad83f..f87c86668 100644 --- a/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt +++ b/src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt @@ -1178,11 +1178,10 @@ object MEExpressionCompletionUtil { private fun addDefinition(context: InsertionContext, id: String, definitionValue: String): PsiAnnotation? { val contextElement = context.file.findElementAt(context.startOffset) ?: return null - return addDefinition(contextElement, id, definitionValue) + return addDefinition(context.project, contextElement, id, definitionValue) } - fun addDefinition(contextElement: PsiElement, id: String, definitionValue: String): PsiAnnotation? { - val project = contextElement.project + fun addDefinition(project: Project, contextElement: PsiElement, id: String, definitionValue: String): PsiAnnotation? { val injectionHost = contextElement.findMultiInjectionHost() ?: return null val expressionAnnotation = injectionHost.parentOfType() ?: return null if (!expressionAnnotation.hasQualifiedName(MixinConstants.MixinExtras.EXPRESSION)) {