Skip to content

Commit ef4e867

Browse files
committed
Merge branch '2025.2' into 2025.3
2 parents b662717 + 1dea1b4 commit ef4e867

17 files changed

Lines changed: 161 additions & 32 deletions

File tree

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ Minecraft Development for IntelliJ
1919
<td align="left">2025.3</td>
2020
<td align="left"><a href="https://ci.mcdev.io/viewType.html?buildTypeId=MinecraftDev_Nightly_20253"><img src="https://ci.mcdev.io/app/rest/builds/buildType:(id:MinecraftDev_Nightly_20253)/statusIcon.svg" alt="2025.3 Nightly Status" /></a></td>
2121
</tr>
22+
<tr>
23+
<td align="left">2026.1</td>
24+
<td align="left"><a href="https://ci.mcdev.io/viewType.html?buildTypeId=MinecraftDev_Nightly_20261"><img src="https://ci.mcdev.io/app/rest/builds/buildType:(id:MinecraftDev_Nightly_20261)/statusIcon.svg" alt="2026.1 Nightly Status" /></a></td>
25+
</tr>
2226
<tr>
2327
<td align="left"><b>OS Tests</b></td>
2428
<td align="left" colspan="2">

src/gradle-tooling-extension/groovy/com/demonwav/mcdev/platform/mcp/gradle/tooling/neomoddev/NeoModDevGradleModelBuilderImpl.groovy

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ final class NeoModDevGradleModelBuilderImpl implements ModelBuilderService {
5151

5252
def neoFormVersion
5353
try {
54-
neoFormVersion = extension.neoFormVersion.getOrNull()
54+
def neoFormVersionProp = extension.neoFormVersion
55+
if (neoFormVersionProp instanceof String) {
56+
neoFormVersion = neoFormVersionProp
57+
} else if (neoFormVersionProp instanceof Provider) {
58+
neoFormVersion = neoFormVersionProp.getOrNull()
59+
} else {
60+
neoFormVersion = null
61+
}
5562
} catch (InvalidUserCodeException ignore) {
5663
// Happens when the NeoForm version is not set
5764
neoFormVersion = null

src/main/grammars/CtLexer.flex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import static com.intellij.psi.TokenType.*;
4646
%s AW_TYPES
4747
%s ITF_ENTRY_KEY
4848
%s ITF_ENTRY_VALUE
49+
%s ENUM_NAME
50+
%s ENUM_VALUE
4951
%s SIGNATURE
5052

5153
%unicode
@@ -59,6 +61,7 @@ SIGNATURE_CLASS_VALUE_START=L[^;<\n]+
5961
TYPE_VARIABLE=T[^;\n]+;
6062
ACCESS_ELEMENT=accessible|transitive-accessible|extendable|transitive-extendable|mutable|transitive-mutable
6163
INJECT_INTERFACE_ELEMENT=inject-interface|transitive-inject-interface
64+
EXTEND_ENUM_ELEMENT=extend-enum|transitive-extend-enum
6265
CLASS_ELEMENT=class
6366
METHOD_ELEMENT=method
6467
FIELD_ELEMENT=field
@@ -74,6 +77,7 @@ WHITE_SPACE=\s
7477
{HEADER_NAME} { yybegin(HEADER); return HEADER_NAME; }
7578
{ACCESS_ELEMENT} { yybegin(AW_ENTRY); return ACCESS_ELEMENT; }
7679
{INJECT_INTERFACE_ELEMENT} { yybegin(ITF_ENTRY_KEY); return INJECT_INTERFACE_ELEMENT; }
80+
{EXTEND_ENUM_ELEMENT} { yybegin(ENUM_NAME); return EXTEND_ENUM_ELEMENT; }
7781
}
7882

7983
<HEADER> {
@@ -111,6 +115,14 @@ WHITE_SPACE=\s
111115
{CLASS_NAME_ELEMENT} { yybegin(SIGNATURE); return CLASS_NAME_ELEMENT; }
112116
}
113117

118+
<ENUM_NAME> {
119+
{CLASS_NAME_ELEMENT} { yybegin(ENUM_VALUE); return CLASS_NAME_ELEMENT; }
120+
}
121+
122+
<ENUM_VALUE> {
123+
{NAME_ELEMENT} { return NAME_ELEMENT; }
124+
}
125+
114126
<SIGNATURE> {
115127
"<" { return LESS_THAN; }
116128
">" { return GREATER_THAN; }

src/main/grammars/CtParser.bnf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ header ::= HEADER_NAME HEADER_VERSION_ELEMENT HEADER_NAMESPACE_ELEMENT {
4949
pin = 1
5050
}
5151

52-
private entry ::= aw_entry | itf_entry {
52+
private entry ::= aw_entry | itf_entry | extend_enum_entry {
5353
mixin="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.impl.CtEntryImplMixin"
5454
implements="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.impl.CtEntryMixin"
5555
recoverWhile = line_recover
@@ -84,6 +84,12 @@ itf_entry ::= INJECT_INTERFACE_ELEMENT class_name signature {
8484
pin = 1
8585
}
8686

87+
extend_enum_entry ::= EXTEND_ENUM_ELEMENT class_name member_name {
88+
mixin="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.impl.CtExtendEnumEntryImplMixin"
89+
implements="com.demonwav.mcdev.platform.mcp.ct.psi.mixins.CtExtendEnumEntryMixin"
90+
pin = 1
91+
}
92+
8793
private line_recover ::= !(end_line | COMMENT)
8894

8995
access ::= ACCESS_ELEMENT {

src/main/kotlin/facet/MinecraftFacet.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.intellij.facet.FacetManager
3535
import com.intellij.facet.FacetTypeId
3636
import com.intellij.facet.FacetTypeRegistry
3737
import com.intellij.ide.projectView.ProjectView
38+
import com.intellij.openapi.application.ApplicationManager
3839
import com.intellij.openapi.application.runReadAction
3940
import com.intellij.openapi.application.runWriteAction
4041
import com.intellij.openapi.module.Module
@@ -77,7 +78,15 @@ class MinecraftFacet(
7778
roots.clear()
7879
}
7980

80-
fun refresh() = runWriteActionAndWait {
81+
fun refresh() {
82+
refreshWritePhase()
83+
// Refresh the project view separately to not hold the write lock
84+
ApplicationManager.getApplication().invokeLater {
85+
ProjectView.getInstance(module.project).refresh()
86+
}
87+
}
88+
89+
private fun refreshWritePhase() = runWriteActionAndWait {
8190
if (module.isDisposed) {
8291
return@runWriteActionAndWait
8392
}
@@ -118,8 +127,6 @@ class MinecraftFacet(
118127

119128
newlyEnabled.forEach(AbstractModule::init)
120129
modules.forEach(AbstractModule::refresh)
121-
122-
ProjectView.getInstance(module.project).refresh()
123130
}
124131

125132
private fun updateRoots() = runWriteAction {

src/main/kotlin/platform/mcp/McpModule.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.demonwav.mcdev.platform.mcp.util.McpConstants
2929
import com.demonwav.mcdev.translations.TranslationFileListener
3030
import com.demonwav.mcdev.util.runWriteTaskLater
3131
import com.intellij.json.JsonFileType
32+
import com.intellij.openapi.application.ApplicationManager
3233
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
3334
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
3435
import com.intellij.openapi.fileTypes.FileTypeManager
@@ -71,7 +72,10 @@ class McpModule(facet: MinecraftFacet) : AbstractModule(facet) {
7172
}
7273

7374
if (requiresRefresh) {
74-
ExternalSystemUtil.refreshProjects(ImportSpecBuilder(project, GradleConstants.SYSTEM_ID))
75+
// Schedule Gradle project refresh outside write lock
76+
ApplicationManager.getApplication().invokeLater {
77+
ExternalSystemUtil.refreshProjects(ImportSpecBuilder(project, GradleConstants.SYSTEM_ID))
78+
}
7579
}
7680
}
7781
}

src/main/kotlin/platform/mcp/ct/CtAnnotator.kt

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ package com.demonwav.mcdev.platform.mcp.ct
2222

2323
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtAccess
2424
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtClassLiteral
25+
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtExtendEnumEntry
2526
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtFieldLiteral
2627
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtHeader
28+
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtItfEntry
2729
import com.demonwav.mcdev.platform.mcp.ct.gen.psi.CtMethodLiteral
2830
import com.demonwav.mcdev.util.childOfType
2931
import com.google.common.collect.HashMultimap
@@ -38,23 +40,40 @@ import com.intellij.psi.util.PsiTreeUtil
3840
class CtAnnotator : Annotator {
3941

4042
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
41-
if (element is CtAccess) {
42-
val access = element.text
43-
val target = PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java)?.text
44-
if (!TokenSets.compatibleByAccessMap.get(access).contains(target)) {
45-
holder.newAnnotation(HighlightSeverity.ERROR, "Access '$access' cannot be used on '$target'").create()
46-
}
43+
val effectiveVersion by lazy { element.containingFile?.childOfType<CtHeader>()?.effectiveVersion ?: 1 }
44+
45+
when (element) {
46+
is CtAccess -> {
47+
val access = element.text
48+
val target = PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java)?.text
49+
if (!TokenSets.compatibleByAccessMap.get(access).contains(target)) {
50+
holder.newAnnotation(HighlightSeverity.ERROR, "Access '$access' cannot be used on '$target'").create()
51+
}
4752

48-
if (element.accessElement.text.startsWith("transitive-") &&
49-
element.containingFile?.childOfType<CtHeader>()?.effectiveVersion == 1
50-
) {
51-
holder.newAnnotation(HighlightSeverity.ERROR, "Transitive accesses were introduced in v2").create()
53+
if (element.accessElement.text.startsWith("transitive-") && effectiveVersion < 2) {
54+
holder.newAnnotation(HighlightSeverity.ERROR, "Transitive accesses were introduced in v2").create()
55+
}
56+
}
57+
is CtItfEntry -> {
58+
if (effectiveVersion < 3) {
59+
holder.newAnnotation(HighlightSeverity.ERROR, "Interface injection was introduced in ClassTweaker v1")
60+
.range(element.firstChild)
61+
.create()
62+
}
63+
}
64+
is CtExtendEnumEntry -> {
65+
if (effectiveVersion < 4) {
66+
holder.newAnnotation(HighlightSeverity.ERROR, "Enum extension was introduced in ClassTweaker v2")
67+
.range(element.firstChild)
68+
.create()
69+
}
5270
}
53-
} else if (element is CtFieldLiteral || element is CtMethodLiteral || element is CtClassLiteral) {
54-
val target = element.text
55-
val access = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace::class.java)?.text
56-
if (!TokenSets.compatibleByTargetMap.get(target).contains(access)) {
57-
holder.newAnnotation(HighlightSeverity.ERROR, "'$target' cannot be used with '$access'").create()
71+
is CtFieldLiteral, is CtMethodLiteral, is CtClassLiteral -> {
72+
val target = element.text
73+
val access = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace::class.java)?.text
74+
if (!TokenSets.compatibleByTargetMap.get(target).contains(access)) {
75+
holder.newAnnotation(HighlightSeverity.ERROR, "'$target' cannot be used with '$access'").create()
76+
}
5877
}
5978
}
6079
}

src/main/kotlin/platform/mcp/ct/CtColorSettingsPage.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ class CtColorSettingsPage : ColorSettingsPage {
3232
override fun getHighlighter() = CtSyntaxHighlighter()
3333
override fun getDemoText() =
3434
$$"""
35-
classTweaker v1 named
35+
classTweaker v2 named
3636
37-
# https://www.fabricmc.net/wiki/tutorial:accesswideners
37+
# https://docs.fabricmc.net/develop/class-tweakers/
3838
3939
extendable class net/minecraft/world/item/crafting/Ingredient
4040
transitive-extendable class net/minecraft/world/item/crafting/Ingredient
@@ -45,6 +45,8 @@ class CtColorSettingsPage : ColorSettingsPage {
4545
accessible field net/minecraft/world/item/crafting/Ingredient values [Lnet/minecraft/world/item/crafting/Ingredient$Value;
4646
inject-interface net/minecraft/world/level/block/Block com/example/MyBlockInterface
4747
transitive-inject-interface net/minecraft/world/level/block/Block com/example/MyTransitiveBlockInterface<TT;>
48+
extend-enum net/minecraft/client/gui/Gui$HeartType MYMOD_HEART_TYPE
49+
transitive-extend-enum net/minecraft/client/gui/Gui$HeartType MYMOD_TRANSITIVE_HEART_TYPE
4850
""".trimIndent()
4951

5052
override fun getAdditionalHighlightingTagToDescriptorMap(): Map<String, TextAttributesKey>? = null
@@ -58,6 +60,7 @@ class CtColorSettingsPage : ColorSettingsPage {
5860
AttributesDescriptor("Header namespace", CtSyntaxHighlighter.HEADER_NAMESPACE),
5961
AttributesDescriptor("Access", CtSyntaxHighlighter.ACCESS),
6062
AttributesDescriptor("Inject interface", CtSyntaxHighlighter.INJECT_INTERFACE),
63+
AttributesDescriptor("Extend enum", CtSyntaxHighlighter.EXTEND_ENUM),
6164
AttributesDescriptor("Class element", CtSyntaxHighlighter.CLASS_ELEMENT),
6265
AttributesDescriptor("Method element", CtSyntaxHighlighter.METHOD_ELEMENT),
6366
AttributesDescriptor("Field element", CtSyntaxHighlighter.FIELD_ELEMENT),

src/main/kotlin/platform/mcp/ct/CtCompletionContributor.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ object CtEntryStartCompletionProvider : CompletionProvider<CompletionParameters>
111111
"mutable",
112112
"transitive-mutable",
113113
"inject-interface",
114-
"transitive-inject-interface"
114+
"transitive-inject-interface",
115+
"extend-enum",
116+
"transitive-extend-enum",
115117
).map { LookupElementBuilder.create(it).withInsertHandler { ctx, _ -> insertWhitespace(ctx) } }
116118
result.addAllElements(elements)
117119
}

src/main/kotlin/platform/mcp/ct/CtSyntaxHighlighter.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CtSyntaxHighlighter : SyntaxHighlighterBase() {
3838
CtTypes.HEADER_NAMESPACE_ELEMENT -> HEADER_NAMESPACE_KEYS
3939
CtTypes.ACCESS_ELEMENT -> ACCESS_KEYS
4040
CtTypes.INJECT_INTERFACE_ELEMENT -> INJECT_INTERFACE_KEYS
41+
CtTypes.EXTEND_ENUM_ELEMENT -> EXTEND_ENUM_KEYS
4142
CtTypes.CLASS_ELEMENT -> CLASS_ELEMENT_KEYS
4243
CtTypes.METHOD_ELEMENT -> METHOD_ELEMENT_KEYS
4344
CtTypes.FIELD_ELEMENT -> FIELD_ELEMENT_KEYS
@@ -60,6 +61,8 @@ class CtSyntaxHighlighter : SyntaxHighlighterBase() {
6061
TextAttributesKey.createTextAttributesKey("CT_ACCESS", DefaultLanguageHighlighterColors.KEYWORD)
6162
val INJECT_INTERFACE =
6263
TextAttributesKey.createTextAttributesKey("CT_INJECT_INTERFACE", DefaultLanguageHighlighterColors.KEYWORD)
64+
val EXTEND_ENUM =
65+
TextAttributesKey.createTextAttributesKey("CT_EXTEND_ENUM", DefaultLanguageHighlighterColors.KEYWORD)
6366
val CLASS_ELEMENT =
6467
TextAttributesKey.createTextAttributesKey("CT_CLASS_ELEMENT", DefaultLanguageHighlighterColors.KEYWORD)
6568
val METHOD_ELEMENT =
@@ -85,6 +88,7 @@ class CtSyntaxHighlighter : SyntaxHighlighterBase() {
8588
private val HEADER_NAMESPACE_KEYS = arrayOf(HEADER_NAMESPACE)
8689
private val ACCESS_KEYS = arrayOf(ACCESS)
8790
private val INJECT_INTERFACE_KEYS = arrayOf(INJECT_INTERFACE)
91+
private val EXTEND_ENUM_KEYS = arrayOf(EXTEND_ENUM)
8892
private val CLASS_ELEMENT_KEYS = arrayOf(CLASS_ELEMENT)
8993
private val METHOD_ELEMENT_KEYS = arrayOf(METHOD_ELEMENT)
9094
private val FIELD_ELEMENT_KEYS = arrayOf(FIELD_ELEMENT)

0 commit comments

Comments
 (0)