diff --git a/src/main/grammars/AwParser.bnf b/src/main/grammars/AwParser.bnf index 99d5f79c1..597e8513c 100644 --- a/src/main/grammars/AwParser.bnf +++ b/src/main/grammars/AwParser.bnf @@ -58,12 +58,12 @@ class_entry ::= access class_literal class_name { implements="com.demonwav.mcdev.platform.mcp.aw.psi.mixins.AwClassEntryMixin" } -method_entry ::= access method_literal class_name member_name method_desc{ +method_entry ::= access method_literal class_name member_name method_desc { mixin="com.demonwav.mcdev.platform.mcp.aw.psi.mixins.impl.AwMethodEntryImplMixin" implements="com.demonwav.mcdev.platform.mcp.aw.psi.mixins.AwMethodEntryMixin" } -field_entry ::= access field_literal class_name member_name field_desc{ +field_entry ::= access field_literal class_name member_name field_desc { mixin="com.demonwav.mcdev.platform.mcp.aw.psi.mixins.impl.AwFieldEntryImplMixin" implements="com.demonwav.mcdev.platform.mcp.aw.psi.mixins.AwFieldEntryMixin" } diff --git a/src/main/kotlin/platform/mcp/aw/AwAnnotator.kt b/src/main/kotlin/platform/mcp/aw/AwAnnotator.kt index 76a6c2683..0f011873a 100644 --- a/src/main/kotlin/platform/mcp/aw/AwAnnotator.kt +++ b/src/main/kotlin/platform/mcp/aw/AwAnnotator.kt @@ -20,19 +20,26 @@ package com.demonwav.mcdev.platform.mcp.aw +import com.demonwav.mcdev.asset.MCDevBundle +import com.demonwav.mcdev.platform.mcp.aw.config.IgnoredClassNamesConfig import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwAccess import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwClassLiteral +import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwClassName import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwFieldLiteral import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwHeader import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwMethodLiteral +import com.demonwav.mcdev.platform.mcp.aw.quickfix.IgnoreClassWarningFix import com.demonwav.mcdev.util.childOfType import com.google.common.collect.HashMultimap import com.google.common.collect.Multimaps import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.Annotator import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.project.DumbService +import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiElement import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.PsiTreeUtil class AwAnnotator : Annotator { @@ -56,6 +63,28 @@ class AwAnnotator : Annotator { if (!compatibleByTargetMap.get(target).contains(access)) { holder.newAnnotation(HighlightSeverity.ERROR, "'$target' cannot be used with '$access'").create() } + } else if (element is AwClassName) { + val project = element.project + + if (DumbService.isDumb(project)) { + return + } + + val javaPsiFacade = JavaPsiFacade.getInstance(project) + val scope = GlobalSearchScope.allScope(project) + + val classFqn = element.text.replace('/', '.') + val psiClass = javaPsiFacade.findClass(classFqn, scope) + + val config = IgnoredClassNamesConfig.getInstance(project) + val ignored = config.ignoredClassNames + + if (classFqn !in ignored && psiClass == null) { + holder.newAnnotation(HighlightSeverity.WARNING, MCDevBundle("inspection.aw.class_not_found", classFqn)) + .range(element.textRange) + .withFix(IgnoreClassWarningFix(classFqn, element)) + .create() + } } } diff --git a/src/main/kotlin/platform/mcp/aw/config/IgnoredClassNamesConfig.kt b/src/main/kotlin/platform/mcp/aw/config/IgnoredClassNamesConfig.kt new file mode 100644 index 000000000..d147054c0 --- /dev/null +++ b/src/main/kotlin/platform/mcp/aw/config/IgnoredClassNamesConfig.kt @@ -0,0 +1,32 @@ +package com.demonwav.mcdev.platform.mcp.aw.config + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.project.Project + +@Service(Service.Level.PROJECT) +@State(name = "IgnoredClassNamesConfig", storages = [Storage("ignoredClassNames.xml")]) +class IgnoredClassNamesConfig : PersistentStateComponent { + + data class State(var ignoredClassNames: MutableSet = mutableSetOf()) + + private var state = State() + + companion object { + fun getInstance(project: Project): IgnoredClassNamesConfig = + project.getService(IgnoredClassNamesConfig::class.java) + } + + var ignoredClassNames: MutableSet + get() = state.ignoredClassNames + set(value) { + state.ignoredClassNames = value + } + + override fun getState(): State = state + override fun loadState(state: State) { + this.state = state + } +} diff --git a/src/main/kotlin/platform/mcp/aw/quickfix/IgnoreClassWarningFix.kt b/src/main/kotlin/platform/mcp/aw/quickfix/IgnoreClassWarningFix.kt new file mode 100644 index 000000000..4a617ba04 --- /dev/null +++ b/src/main/kotlin/platform/mcp/aw/quickfix/IgnoreClassWarningFix.kt @@ -0,0 +1,38 @@ +package com.demonwav.mcdev.platform.mcp.aw.quickfix + +import com.demonwav.mcdev.platform.mcp.aw.config.IgnoredClassNamesConfig +import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwClassName +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.LocalQuickFixOnPsiElement +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.util.IntentionFamilyName +import com.intellij.codeInspection.util.IntentionName +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.Nls + +class IgnoreClassWarningFix(private val className: String, private val element: PsiElement) : IntentionAction { + + override fun getText() = "Ignore warnings for '$className'" + + override fun getFamilyName() = "Ignore warnings" + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean = element.isValid + + override fun invoke(project: Project, editor: Editor?, file: PsiFile?) { + val config = IgnoredClassNamesConfig.getInstance(project) + val ignored = config.ignoredClassNames.toMutableSet() + ignored.add(className) + config.ignoredClassNames = ignored + + file?.let { + DaemonCodeAnalyzer.getInstance(project).restart(it) + } + } + + override fun startInWriteAction(): Boolean = false +} \ No newline at end of file diff --git a/src/main/resources/messages/MinecraftDevelopment.properties b/src/main/resources/messages/MinecraftDevelopment.properties index 15925cea1..b4b2cb04c 100644 --- a/src/main/resources/messages/MinecraftDevelopment.properties +++ b/src/main/resources/messages/MinecraftDevelopment.properties @@ -199,6 +199,8 @@ inspection.entity_data_param.description=Reports when the class passed to an ent inspection.entity_data_param.message=Entity class does not match this entity class inspection.entity_data_param.fix=Replace other entity class with this entity class +inspection.aw.class_not_found=Class "{0}" not found in the project or libraries. Please make sure the class name is correct and the relevant module dependencies are set. + nbt.compression.gzip=GZipped nbt.compression.uncompressed=Uncompressed nbt.compression.file_type.label=Compression: