Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/llamas expressions fixes #2324

Merged
Show file tree
Hide file tree
Changes from 11 commits
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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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:2ad48e8"

implementation(mixinExtras("expressions"))
testLibs(mixinExtras("common"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
)
Expand Down
49 changes: 49 additions & 0 deletions src/main/kotlin/platform/mixin/expression/MEExpressionAnnotator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -275,6 +283,7 @@ class MEExpressionAnnotator : Annotator {
)
.range(type)
.highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
.withDefinitionFix(type)
.create()
} else {
holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES)
Expand Down Expand Up @@ -306,6 +315,7 @@ class MEExpressionAnnotator : Annotator {
)
.range(variable)
.highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
.withDefinitionFix(variable)
.create()
} else {
holder.newSilentAnnotation(HighlightSeverity.TEXT_ATTRIBUTES)
Expand All @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -691,11 +691,12 @@ object MEExpressionCompletionUtil {
canCompleteExprs: Boolean,
canCompleteTypes: Boolean
): List<EliminableLookup> {
val flow = flows[insn]
when (insn.insn) {
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")
Expand Down Expand Up @@ -727,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())
Expand All @@ -743,12 +744,10 @@ 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)
.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) {
Expand All @@ -766,25 +765,21 @@ 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<InstantiationInfo>(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}"))
Expand All @@ -794,52 +789,35 @@ 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(
LookupElementBuilder.create(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 -> {
when (insn.insn.opcode) {
Opcodes.ARRAYLENGTH -> {
if (canCompleteExprs) {
return listOf(
LookupElementBuilder.create("length")
createUniqueLookup("length")
.withIcon(PlatformIcons.FIELD_ICON)
.withTypeText("int")
.createEliminable("arraylength")
Expand Down Expand Up @@ -868,7 +846,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())
Expand Down Expand Up @@ -935,7 +913,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())

Expand All @@ -946,6 +924,22 @@ object MEExpressionCompletionUtil {
}
}

private fun createNewArrayCompletion(flow: FlowValue?, arrayType: Type): List<EliminableLookup> {
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,
Expand Down Expand Up @@ -997,7 +991,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(
Expand All @@ -1020,14 +1014,19 @@ 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)
.createEliminable("local $localName", if (isImplicit) -1 else 0)
)
}

private fun LookupElementBuilder.withDescTailText(desc: String) =
withTailText(
Type.getArgumentTypes(desc).joinToString(prefix = "(", postfix = ")") { it.presentableName() }
)

private fun LookupElement.withTail(tailType: TailType?) = object : TailTypeDecorator<LookupElement>(this) {
override fun computeTailType(context: InsertionContext?) = tailType
}
Expand Down Expand Up @@ -1179,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
LlamaLad7 marked this conversation as resolved.
Show resolved Hide resolved
val injectionHost = contextElement.findMultiInjectionHost() ?: return null
val expressionAnnotation = injectionHost.parentOfType<PsiAnnotation>() ?: return null
if (!expressionAnnotation.hasQualifiedName(MixinConstants.MixinExtras.EXPRESSION)) {
Expand All @@ -1196,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
}
Expand All @@ -1212,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)
}

Expand Down Expand Up @@ -1271,13 +1275,25 @@ 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
}
}
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
}

private fun createUniqueLookup(text: String) = LookupElementBuilder.create(Any(), text)
Earthcomputer marked this conversation as resolved.
Show resolved Hide resolved

private fun LookupElement.createEliminable(uniquenessKey: String, priority: Int = 0) =
EliminableLookup(uniquenessKey, this, priority)

Expand Down
Loading
Loading