User: joe Date: 08 Nov 25 00:29 Revision: 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6 Summary: Don't hardcode namespaces for CT files; don't assume `LoomExtension` has a `mappingsFile` property TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10245&personal=false Index: src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelBuilderImpl.groovy =================================================================== --- src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelBuilderImpl.groovy (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelBuilderImpl.groovy (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -61,7 +61,7 @@ FabricLoomModel build(Project project, Object loomExtension) { def minecraftVersion = loomExtension.minecraftProvider.minecraftVersion() - def tinyMappings = loomExtension.mappingsFile + def tinyMappings = loomExtension.hasProperty("mappingsFile") ? loomExtension.mappingsFile : null def splitMinecraftJar = loomExtension.areEnvironmentSourceSetsSplit() def decompilers = [:] Index: src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelImpl.groovy =================================================================== --- src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelImpl.groovy (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModelImpl.groovy (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -21,10 +21,12 @@ package com.demonwav.mcdev.platform.mcp.gradle.tooling.fabricloom import groovy.transform.Immutable +import org.jetbrains.annotations.Nullable @Immutable(knownImmutableClasses = [File]) class FabricLoomModelImpl implements FabricLoomModel, Serializable { String minecraftVersion + @Nullable File tinyMappings Map> decompilers boolean splitMinecraftJar Index: src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModel.java =================================================================== --- src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModel.java (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/gradle-tooling-extension/java/com/demonwav/mcdev/platform/mcp/gradle/tooling/fabricloom/FabricLoomModel.java (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -20,6 +20,8 @@ package com.demonwav.mcdev.platform.mcp.gradle.tooling.fabricloom; +import org.jetbrains.annotations.Nullable; + import java.io.File; import java.util.List; import java.util.Map; @@ -28,6 +30,7 @@ String getMinecraftVersion(); + @Nullable File getTinyMappings(); Map> getDecompilers(); Index: src/main/grammars/CtLexer.flex =================================================================== --- src/main/grammars/CtLexer.flex (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/grammars/CtLexer.flex (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -52,7 +52,7 @@ HEADER_NAME=accessWidener|classTweaker HEADER_VERSION_ELEMENT=v\d+ -HEADER_NAMESPACE_ELEMENT=named|intermediary +HEADER_NAMESPACE_ELEMENT=\w+ PRIMITIVE=[ZBCSIFDJV] CLASS_VALUE=L[^;\n]+; SIGNATURE_CLASS_VALUE_START=L[^;<\n]+ Index: src/main/grammars/CtParser.bnf =================================================================== --- src/main/grammars/CtParser.bnf (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/grammars/CtParser.bnf (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -43,6 +43,9 @@ header ::= HEADER_NAME HEADER_VERSION_ELEMENT HEADER_NAMESPACE_ELEMENT { mixin="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.impl.CtHeaderImplMixin" implements="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.CtHeaderMixin" + methods=[ + namespaceElement="HEADER_NAMESPACE_ELEMENT" + ] pin = 1 } Index: src/main/kotlin/platform/fabric/FabricModule.kt =================================================================== --- src/main/kotlin/platform/fabric/FabricModule.kt (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/kotlin/platform/fabric/FabricModule.kt (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -56,6 +56,10 @@ override val namedToMojangManager: MappingsManager? get() = namedToMojangManagerField + private var mappingNamespacesField = emptyList() + val mappingNamespaces: List + get() = mappingNamespacesField + override val moduleType = FabricModuleType override val type = PlatformType.FABRIC override val icon = PlatformAssets.FABRIC_ICON @@ -79,60 +83,74 @@ } override fun refresh() { - namedToMojangManagerField = if (detectYarn()) { + val mappingDetection = detectMappings() + + namedToMojangManagerField = if (mappingDetection.yarnDetected) { MappingsManager.Immediate(HardcodedYarnToMojmap.createMappings()) } else { null } + + mappingNamespacesField = mappingDetection.namespaces } - private fun detectYarn(): Boolean { - val gradleData = GradleUtil.findGradleModuleData(facet.module) ?: return false - val loomData = - gradleData.children.find { it.key == FabricLoomData.KEY }?.data as? FabricLoomData ?: return false - val mappingsFile = loomData.tinyMappings ?: return false + private fun detectMappings(): MappingDetectionResult { + val gradleData = GradleUtil.findGradleModuleData(facet.module) ?: return MappingDetectionResult.DEFAULT + val loomData = gradleData.children.find { it.key == FabricLoomData.KEY }?.data as? FabricLoomData + ?: return MappingDetectionResult.DEFAULT + val mappingsFile = loomData.tinyMappings ?: return MappingDetectionResult.DEFAULT + val result = MappingDetectionResult() + + try { + MappingReader.read(mappingsFile.toPath(), result) + } catch (_: IOException) { + return MappingDetectionResult.DEFAULT + } + + return result + } + + override fun dispose() { + super.dispose() + fabricJson = null + } + + private class MappingDetectionResult : MappingVisitor { var yarnDetected = false - val visitor = object : MappingVisitor { + var namespaces = emptyList() - private var namedIndex = -1 + private var namedIndex = -1 - override fun visitNamespaces(srcNamespace: String?, dstNamespaces: List) { - namedIndex = dstNamespaces.indexOf("named") + override fun visitNamespaces(srcNamespace: String?, dstNamespaces: List) { + namedIndex = dstNamespaces.indexOf("named") + namespaces = listOfNotNull(srcNamespace) + dstNamespaces - } + } - override fun visitContent() = namedIndex >= 0 + override fun visitContent() = namedIndex >= 0 - override fun visitClass(srcName: String) = true + override fun visitClass(srcName: String) = true - override fun visitField(srcName: String?, srcDesc: String?) = false + override fun visitField(srcName: String?, srcDesc: String?) = false - override fun visitMethod(srcName: String?, srcDesc: String?) = false + override fun visitMethod(srcName: String?, srcDesc: String?) = false - override fun visitMethodArg(argPosition: Int, lvIndex: Int, srcName: String?) = false + override fun visitMethodArg(argPosition: Int, lvIndex: Int, srcName: String?) = false - override fun visitMethodVar(lvtRowIndex: Int, lvIndex: Int, startOpIdx: Int, srcName: String?) = false + override fun visitMethodVar(lvtRowIndex: Int, lvIndex: Int, startOpIdx: Int, srcName: String?) = false - override fun visitDstName(targetKind: MappedElementKind?, namespace: Int, name: String) { - if (namespace == namedIndex && name == "net/minecraft/client/MinecraftClient") { - yarnDetected = true - } - } + override fun visitDstName(targetKind: MappedElementKind?, namespace: Int, name: String) { + if (namespace == namedIndex && name == "net/minecraft/client/MinecraftClient") { + yarnDetected = true + } + } - override fun visitComment(targetKind: MappedElementKind?, comment: String?) { - } + override fun visitComment(targetKind: MappedElementKind?, comment: String?) { + } - } - try { - MappingReader.read(mappingsFile.toPath(), visitor) - } catch (e: IOException) { - return false + companion object { + val DEFAULT = MappingDetectionResult().apply { + namespaces = listOf("official") - } + } - - return yarnDetected - } + } - - override fun dispose() { - super.dispose() - fabricJson = null } } Index: src/main/kotlin/platform/mcp/ct/CtCompletionContributor.kt =================================================================== --- src/main/kotlin/platform/mcp/ct/CtCompletionContributor.kt (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/kotlin/platform/mcp/ct/CtCompletionContributor.kt (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -20,7 +20,10 @@ package com.demonwav.mcdev.platform.mcp.ct +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.platform.fabric.FabricModuleType import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtTypes +import com.demonwav.mcdev.util.findModule import com.intellij.codeInsight.completion.CodeCompletionHandlerBase import com.intellij.codeInsight.completion.CompletionContributor import com.intellij.codeInsight.completion.CompletionParameters @@ -86,8 +89,12 @@ parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet, - ) = result.addAllElements(listOf("named", "intermediary").map(LookupElementBuilder::create)) + ) { + val module = parameters.originalFile.findModule() ?: return + val fabricModule = MinecraftFacet.getInstance(module, FabricModuleType) ?: return + result.addAllElements(fabricModule.mappingNamespaces.map(LookupElementBuilder::create)) -} + } +} object CtEntryStartCompletionProvider : CompletionProvider() { Index: src/main/kotlin/platform/mcp/ct/CtSyntaxHighlighter.kt =================================================================== --- src/main/kotlin/platform/mcp/ct/CtSyntaxHighlighter.kt (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/kotlin/platform/mcp/ct/CtSyntaxHighlighter.kt (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -55,7 +55,7 @@ val HEADER_NAME = TextAttributesKey.createTextAttributesKey("CT_HEADER_NAME", DefaultLanguageHighlighterColors.KEYWORD) val HEADER_NAMESPACE = - TextAttributesKey.createTextAttributesKey("CT_HEADER_NAMESPACE", DefaultLanguageHighlighterColors.KEYWORD) + TextAttributesKey.createTextAttributesKey("CT_HEADER_NAMESPACE", DefaultLanguageHighlighterColors.CLASS_REFERENCE) val ACCESS = TextAttributesKey.createTextAttributesKey("CT_ACCESS", DefaultLanguageHighlighterColors.KEYWORD) val INJECT_INTERFACE = Index: src/main/kotlin/platform/mcp/ct/inspections/UnknownCtNamespaceInspection.kt =================================================================== --- src/main/kotlin/platform/mcp/ct/inspections/UnknownCtNamespaceInspection.kt (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) +++ src/main/kotlin/platform/mcp/ct/inspections/UnknownCtNamespaceInspection.kt (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -0,0 +1,51 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 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.mcp.ct.inspections + +import com.demonwav.mcdev.facet.MinecraftFacet +import com.demonwav.mcdev.platform.fabric.FabricModuleType +import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtHeader +import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtVisitor +import com.demonwav.mcdev.platform.mcp.ct.psi.mixins.CtHeaderMixin +import com.demonwav.mcdev.util.findModule +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.ProblemsHolder + +class UnknownCtNamespaceInspection : LocalInspectionTool() { + override fun getDisplayName() = "Unknown CT namespace" + override fun getStaticDescription() = "Reports an unknown namespace in a ClassTweaker header" + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : CtVisitor() { + override fun visitHeaderMixin(header: CtHeaderMixin) { + val namespace = header.namespaceString ?: return + + val module = header.findModule() ?: return + val fabricModule = MinecraftFacet.getInstance(module, FabricModuleType) ?: return + + if (namespace !in fabricModule.mappingNamespaces) { + holder.registerProblem( + (header as CtHeader).namespaceElement ?: header, + "Unrecognized namespace" + ) + } + } + } +} Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 76e69e4e1bfa46bab8275381158723d7424d5c6e) +++ src/main/resources/META-INF/plugin.xml (revision 1a1fc0374c31f7ba9ddf380cbe5f9e5bc8d915f6) @@ -1027,6 +1027,13 @@ level="WARNING" hasStaticDescription="true" implementationClass="com.demonwav.mcdev.platform.mcp.ct.inspections.DuplicateAwEntryInspection"/> +