Skip to content

Commit c6f35ea

Browse files
committed
Implement CT v2 (enum extensions)
1 parent 8124072 commit c6f35ea

File tree

8 files changed

+114
-19
lines changed

8 files changed

+114
-19
lines changed

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/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)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2025 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.ct.psi.mixins
22+
23+
interface CtExtendEnumEntryMixin : CtEntryMixin
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2025 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.ct.psi.mixins.impl
22+
23+
import com.demonwav.mcdev.platform.mcp.ct.psi.mixins.CtExtendEnumEntryMixin
24+
import com.intellij.lang.ASTNode
25+
26+
abstract class CtExtendEnumEntryImplMixin(node: ASTNode): CtEntryImplMixin(node), CtExtendEnumEntryMixin

0 commit comments

Comments
 (0)