Skip to content

Commit 72dcdc9

Browse files
committed
Merge branch '2024.3' into 2025.1
2 parents deb7fb9 + 4cd5d91 commit 72dcdc9

18 files changed

+691
-102
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ fuel-coroutines = { module = "com.github.kittinunf.fuel:fuel-coroutines", versio
6060
test-mockJdk = "org.jetbrains.idea:mock-jdk:1.7-4d76c50"
6161
test-mixin = "org.spongepowered:mixin:0.8.5"
6262
test-spigotapi = "org.spigotmc:spigot-api:1.21-R0.1-SNAPSHOT"
63-
test-bungeecord = "net.md-5:bungeecord-api:1.21-R0.1-SNAPSHOT"
63+
test-bungeecord = "net.md-5:bungeecord-api:1.21-R0.3-SNAPSHOT"
6464
test-spongeapi = "org.spongepowered:spongeapi:7.4.0"
6565
test-fabricloader = "net.fabricmc:fabric-loader:0.15.11"
6666
test-nbt = "com.demonwav.mcdev:all-types-nbt:1.0"

src/main/kotlin/platform/mcp/at/completion/AtCompletionContributor.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.demonwav.mcdev.platform.mcp.at.gen.psi.AtTypes
3131
import com.demonwav.mcdev.util.anonymousElements
3232
import com.demonwav.mcdev.util.fullQualifiedName
3333
import com.demonwav.mcdev.util.getSimilarity
34+
import com.demonwav.mcdev.util.localClasses
3435
import com.demonwav.mcdev.util.nameAndParameterTypes
3536
import com.demonwav.mcdev.util.qualifiedMemberReference
3637
import com.demonwav.mcdev.util.simpleQualifiedMemberReference
@@ -157,6 +158,17 @@ class AtCompletionContributor : CompletionContributor() {
157158
),
158159
)
159160
}
161+
162+
for (localClass in currentClass.localClasses) {
163+
val name = localClass.fullQualifiedName ?: continue
164+
result.addElement(
165+
PrioritizedLookupElement.withPriority(
166+
LookupElementBuilder.create(name).withIcon(PlatformIcons.CLASS_ICON),
167+
1.0,
168+
),
169+
)
170+
}
171+
160172
return
161173
}
162174

src/main/kotlin/platform/mixin/completion/AtArgsCompletionContributor.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,12 @@ class AtArgsCompletionContributor : CompletionContributor() {
8686
val key = beforeCursor.substring(0, equalsIndex)
8787
val argsValues = injectionPoint.getArgsValues(atAnnotation, key)
8888
var prefix = beforeCursor.substring(equalsIndex + 1)
89-
if (injectionPoint.isArgValueList(atAnnotation, key)) {
90-
prefix = prefix.substringAfterLast(',').trimStart()
89+
val argValueListDelimieter = injectionPoint.getArgValueListDelimiter(atAnnotation, key)
90+
if (argValueListDelimieter != null) {
91+
val delimiterEndIndex = argValueListDelimieter.findAll(prefix).lastOrNull()?.range?.last
92+
if (delimiterEndIndex != null) {
93+
prefix = prefix.substring(delimiterEndIndex + 1).trimStart()
94+
}
9195
}
9296
result.withPrefixMatcher(prefix).addAllElements(
9397
argsValues.map { completion ->
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.mixin.handlers.desugar
22+
23+
import com.demonwav.mcdev.util.cached
24+
import com.intellij.openapi.project.Project
25+
import com.intellij.openapi.util.Key
26+
import com.intellij.psi.JavaRecursiveElementWalkingVisitor
27+
import com.intellij.psi.PsiClass
28+
import com.intellij.psi.PsiElement
29+
import com.intellij.psi.PsiFile
30+
import com.intellij.psi.util.PsiTreeUtil
31+
import com.intellij.psi.util.parents
32+
33+
object DesugarUtil {
34+
private val ORIGINAL_ELEMENT_KEY = Key.create<PsiElement>("mcdev.desugar.originalElement")
35+
36+
private val DESUGARERS = arrayOf<Desugarer>(
37+
FieldAssignmentDesugarer,
38+
)
39+
40+
fun getOriginalElement(desugared: PsiElement): PsiElement? {
41+
return desugared.parents(true).firstNotNullOfOrNull { it.getCopyableUserData(ORIGINAL_ELEMENT_KEY) }
42+
}
43+
44+
fun setOriginalElement(desugared: PsiElement, original: PsiElement?) {
45+
desugared.putCopyableUserData(ORIGINAL_ELEMENT_KEY, original)
46+
}
47+
48+
fun desugar(project: Project, clazz: PsiClass): PsiClass {
49+
return clazz.cached {
50+
val desugaredFile = clazz.containingFile.copy() as PsiFile
51+
val desugaredClass = PsiTreeUtil.findSameElementInCopy(clazz, desugaredFile)
52+
setOriginalRecursive(desugaredClass, clazz)
53+
for (desugarer in DESUGARERS) {
54+
desugarer.desugar(project, desugaredClass)
55+
}
56+
desugaredClass
57+
}
58+
}
59+
60+
private fun setOriginalRecursive(desugared: PsiElement, original: PsiElement) {
61+
val desugaredElements = mutableListOf<PsiElement>()
62+
desugared.accept(object : JavaRecursiveElementWalkingVisitor() {
63+
override fun visitElement(element: PsiElement) {
64+
super.visitElement(element)
65+
desugaredElements.add(element)
66+
}
67+
})
68+
69+
val originalElements = mutableListOf<PsiElement>()
70+
original.accept(object : JavaRecursiveElementWalkingVisitor() {
71+
override fun visitElement(element: PsiElement) {
72+
super.visitElement(element)
73+
originalElements.add(element)
74+
}
75+
})
76+
77+
for ((originalElement, desugaredElement) in originalElements.zip(desugaredElements)) {
78+
setOriginalElement(desugaredElement, originalElement)
79+
}
80+
}
81+
82+
fun getOriginalToDesugaredMap(desugared: PsiElement): Map<PsiElement, List<PsiElement>> {
83+
val result = mutableMapOf<PsiElement, MutableList<PsiElement>>()
84+
desugared.accept(object : JavaRecursiveElementWalkingVisitor() {
85+
override fun visitElement(element: PsiElement) {
86+
super.visitElement(element)
87+
getOriginalElement(element)?.let { original ->
88+
result.getOrPut(original) { mutableListOf() } += desugared
89+
}
90+
}
91+
})
92+
return result
93+
}
94+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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.mixin.handlers.desugar
22+
23+
import com.intellij.openapi.project.Project
24+
import com.intellij.psi.PsiClass
25+
26+
interface Desugarer {
27+
fun desugar(project: Project, clazz: PsiClass)
28+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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.mixin.handlers.desugar
22+
23+
import com.demonwav.mcdev.util.constantValue
24+
import com.intellij.openapi.project.Project
25+
import com.intellij.psi.JavaPsiFacade
26+
import com.intellij.psi.PsiAssignmentExpression
27+
import com.intellij.psi.PsiClass
28+
import com.intellij.psi.PsiClassInitializer
29+
import com.intellij.psi.PsiExpressionStatement
30+
import com.intellij.psi.PsiField
31+
import com.intellij.psi.PsiMethod
32+
import com.intellij.psi.PsiModifier
33+
import com.intellij.psi.PsiStatement
34+
import com.intellij.psi.PsiType
35+
import com.intellij.util.JavaPsiConstructorUtil
36+
37+
object FieldAssignmentDesugarer : Desugarer {
38+
override fun desugar(project: Project, clazz: PsiClass) {
39+
val staticStatementsToInsertPre = mutableListOf<PsiStatement>()
40+
val staticStatementsToInsertPost = mutableListOf<PsiStatement>()
41+
val nonStaticStatementsToInsert = mutableListOf<PsiStatement>()
42+
var seenStaticInitializer = false
43+
44+
for (child in clazz.children) {
45+
when (child) {
46+
is PsiField -> {
47+
val initializer = child.initializer ?: continue
48+
49+
if (child.hasModifierProperty(PsiModifier.STATIC)) {
50+
// check if the field is a ConstantValue with no initializer in the bytecode
51+
val constantValue = initializer.constantValue
52+
if (constantValue != null && constantValue !is PsiType) {
53+
continue
54+
}
55+
56+
val fieldInitializer = JavaPsiFacade.getElementFactory(project)
57+
.createStatementFromText("${child.name} = null;", child) as PsiExpressionStatement
58+
(fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer)
59+
DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child))
60+
61+
if (seenStaticInitializer) {
62+
staticStatementsToInsertPost += fieldInitializer
63+
} else {
64+
staticStatementsToInsertPre += fieldInitializer
65+
}
66+
} else {
67+
val fieldInitializer = JavaPsiFacade.getElementFactory(project)
68+
.createStatementFromText("this.${child.name} = null;", child) as PsiExpressionStatement
69+
(fieldInitializer.expression as PsiAssignmentExpression).rExpression!!.replace(initializer)
70+
DesugarUtil.setOriginalElement(fieldInitializer, DesugarUtil.getOriginalElement(child))
71+
72+
nonStaticStatementsToInsert += fieldInitializer
73+
}
74+
75+
initializer.delete()
76+
}
77+
is PsiClassInitializer -> {
78+
if (child.hasModifierProperty(PsiModifier.STATIC)) {
79+
seenStaticInitializer = true
80+
} else {
81+
nonStaticStatementsToInsert += child.body.statements
82+
child.delete()
83+
}
84+
}
85+
}
86+
}
87+
88+
if (staticStatementsToInsertPre.isNotEmpty() || staticStatementsToInsertPost.isNotEmpty()) {
89+
val staticBlock = findStaticBlock(project, clazz)
90+
for (statement in staticStatementsToInsertPre) {
91+
staticBlock.body.addAfter(statement, staticBlock.body.lBrace)
92+
}
93+
for (statement in staticStatementsToInsertPost) {
94+
staticBlock.body.addBefore(statement, staticBlock.body.rBrace)
95+
}
96+
}
97+
98+
if (nonStaticStatementsToInsert.isNotEmpty()) {
99+
for (constructor in findConstructorsCallingSuper(project, clazz)) {
100+
val body = constructor.body ?: continue
101+
val delegateCtorCall = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(constructor)
102+
for (statement in nonStaticStatementsToInsert.asReversed()) {
103+
body.addAfter(statement, delegateCtorCall?.parent ?: body.lBrace)
104+
}
105+
}
106+
}
107+
}
108+
109+
private fun findStaticBlock(project: Project, clazz: PsiClass): PsiClassInitializer {
110+
for (initializer in clazz.initializers) {
111+
if (initializer.hasModifierProperty(PsiModifier.STATIC)) {
112+
return initializer
113+
}
114+
}
115+
116+
val initializer = JavaPsiFacade.getElementFactory(project)
117+
.createClass("class _Dummy_ { static {} }")
118+
.initializers
119+
.first()
120+
DesugarUtil.setOriginalElement(initializer, DesugarUtil.getOriginalElement(clazz))
121+
return clazz.add(initializer) as PsiClassInitializer
122+
}
123+
124+
private fun findConstructorsCallingSuper(project: Project, clazz: PsiClass): List<PsiMethod> {
125+
val className = clazz.name ?: return emptyList()
126+
127+
val constructors = clazz.constructors.filter {
128+
!JavaPsiConstructorUtil.isChainedConstructorCall(
129+
JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(it)
130+
)
131+
}
132+
133+
if (constructors.isNotEmpty()) {
134+
return constructors
135+
}
136+
137+
val constructor = JavaPsiFacade.getElementFactory(project).createConstructor(className)
138+
DesugarUtil.setOriginalElement(constructor, DesugarUtil.getOriginalElement(clazz))
139+
return listOf(clazz.add(constructor) as PsiMethod)
140+
}
141+
}

0 commit comments

Comments
 (0)