20
20
21
21
package com.demonwav.mcdev.platform.fabric.reference
22
22
23
+ import com.demonwav.mcdev.platform.fabric.util.FabricConstants
24
+ import com.demonwav.mcdev.util.fullQualifiedName
23
25
import com.demonwav.mcdev.util.manipulator
24
26
import com.demonwav.mcdev.util.reference.InspectionReference
27
+ import com.intellij.codeInsight.completion.JavaLookupElementBuilder
25
28
import com.intellij.json.psi.JsonStringLiteral
26
29
import com.intellij.openapi.util.TextRange
27
30
import com.intellij.psi.JavaPsiFacade
28
31
import com.intellij.psi.PsiClass
32
+ import com.intellij.psi.PsiClassType
29
33
import com.intellij.psi.PsiElement
30
34
import com.intellij.psi.PsiElementResolveResult
35
+ import com.intellij.psi.PsiField
31
36
import com.intellij.psi.PsiMethod
32
37
import com.intellij.psi.PsiModifier
33
38
import com.intellij.psi.PsiPolyVariantReference
34
39
import com.intellij.psi.PsiReference
35
40
import com.intellij.psi.PsiReferenceBase
36
41
import com.intellij.psi.PsiReferenceProvider
37
42
import com.intellij.psi.ResolveResult
43
+ import com.intellij.psi.search.searches.ClassInheritorsSearch
44
+ import com.intellij.util.ArrayUtil
38
45
import com.intellij.util.IncorrectOperationException
39
46
import com.intellij.util.ProcessingContext
40
47
@@ -46,8 +53,8 @@ object EntryPointReference : PsiReferenceProvider() {
46
53
val manipulator = element.manipulator ? : return PsiReference .EMPTY_ARRAY
47
54
val range = manipulator.getRangeInElement(element)
48
55
val text = element.text.substring(range.startOffset, range.endOffset)
49
- val methodParts = text.split(" ::" , limit = 2 )
50
- val clazzParts = methodParts [0 ].split(" $" , limit = 0 )
56
+ val memberParts = text.split(" ::" , limit = 2 )
57
+ val clazzParts = memberParts [0 ].split(" $" , limit = 0 )
51
58
val references = mutableListOf<Reference >()
52
59
var cursor = - 1
53
60
var innerClassDepth = - 1
@@ -64,12 +71,12 @@ object EntryPointReference : PsiReferenceProvider() {
64
71
)
65
72
cursor + = clazzPart.length
66
73
}
67
- if (methodParts .size == 2 ) {
74
+ if (memberParts .size == 2 ) {
68
75
cursor + = 2
69
76
references.add(
70
77
Reference (
71
78
element,
72
- range.cutOut(TextRange .from(cursor, methodParts [1 ].length)),
79
+ range.cutOut(TextRange .from(cursor, memberParts [1 ].length)),
73
80
innerClassDepth,
74
81
true ,
75
82
),
@@ -81,16 +88,16 @@ object EntryPointReference : PsiReferenceProvider() {
81
88
private fun resolveReference (
82
89
element : JsonStringLiteral ,
83
90
innerClassDepth : Int ,
84
- isMethodReference : Boolean ,
91
+ isMemberReference : Boolean ,
85
92
): Array <PsiElement > {
86
93
val strReference = element.value
87
- val methodParts = strReference.split(" ::" , limit = 2 )
94
+ val memberParts = strReference.split(" ::" , limit = 2 )
88
95
// split at dollar sign for inner class evaluation
89
- val clazzParts = methodParts [0 ].split(" $" , limit = 0 )
96
+ val clazzParts = memberParts [0 ].split(" $" , limit = 0 )
90
97
// this case should only happen if someone misuses the method, better protect against it anyways
91
98
if (innerClassDepth >= clazzParts.size ||
92
99
innerClassDepth + 1 < clazzParts.size &&
93
- isMethodReference
100
+ isMemberReference
94
101
) {
95
102
throw IncorrectOperationException (" Invalid reference" )
96
103
}
@@ -104,27 +111,63 @@ object EntryPointReference : PsiReferenceProvider() {
104
111
if (inner.contains(' .' )) return PsiElement .EMPTY_ARRAY
105
112
clazz = clazz.findInnerClassByName(inner, false ) ? : return PsiElement .EMPTY_ARRAY
106
113
}
107
- return if (isMethodReference ) {
108
- if (methodParts .size == 1 ) {
114
+ return if (isMemberReference ) {
115
+ if (memberParts .size == 1 ) {
109
116
throw IncorrectOperationException (" Invalid reference" )
110
117
}
111
- clazz.methods.filter { method ->
112
- method.name == methodParts[1 ] &&
113
- method.hasModifierProperty(PsiModifier .PUBLIC ) &&
114
- method.hasModifierProperty(PsiModifier .STATIC )
115
- }.toTypedArray()
118
+
119
+ val members = mutableListOf<PsiElement >()
120
+ clazz.fields.filterTo(members) { field ->
121
+ field.name == memberParts[1 ] &&
122
+ field.hasModifierProperty(PsiModifier .PUBLIC ) &&
123
+ field.hasModifierProperty(PsiModifier .STATIC )
124
+ }
125
+
126
+ clazz.methods.filterTo(members) { method ->
127
+ method.name == memberParts[1 ] &&
128
+ method.hasModifierProperty(PsiModifier .PUBLIC )
129
+ }
130
+
131
+ members.toTypedArray()
116
132
} else {
117
133
arrayOf(clazz)
118
134
}
119
135
}
120
136
121
137
fun isEntryPointReference (reference : PsiReference ) = reference is Reference
122
138
123
- private class Reference (
139
+ fun isValidEntrypointClass (element : PsiClass ): Boolean {
140
+ val psiFacade = JavaPsiFacade .getInstance(element.project)
141
+ var inheritsEntrypointInterface = false
142
+ for (entrypoint in FabricConstants .ENTRYPOINTS ) {
143
+ val entrypointClass = psiFacade.findClass(entrypoint, element.resolveScope)
144
+ ? : continue
145
+ if (element.isInheritor(entrypointClass, true )) {
146
+ inheritsEntrypointInterface = true
147
+ break
148
+ }
149
+ }
150
+ return inheritsEntrypointInterface
151
+ }
152
+
153
+ fun isValidEntrypointField (field : PsiField ): Boolean {
154
+ if (! field.hasModifierProperty(PsiModifier .PUBLIC ) || ! field.hasModifierProperty(PsiModifier .STATIC )) {
155
+ return false
156
+ }
157
+
158
+ val fieldTypeClass = (field.type as ? PsiClassType )?.resolve()
159
+ return fieldTypeClass != null && isValidEntrypointClass(fieldTypeClass)
160
+ }
161
+
162
+ fun isValidEntrypointMethod (method : PsiMethod ): Boolean {
163
+ return method.hasModifierProperty(PsiModifier .PUBLIC ) && ! method.hasParameters()
164
+ }
165
+
166
+ class Reference (
124
167
element : JsonStringLiteral ,
125
168
range : TextRange ,
126
169
private val innerClassDepth : Int ,
127
- private val isMethodReference : Boolean ,
170
+ private val isMemberReference : Boolean ,
128
171
) :
129
172
PsiReferenceBase <JsonStringLiteral >(element, range),
130
173
PsiPolyVariantReference ,
@@ -134,7 +177,7 @@ object EntryPointReference : PsiReferenceProvider() {
134
177
override val unresolved = resolve() == null
135
178
136
179
override fun multiResolve (incompleteCode : Boolean ): Array <ResolveResult > {
137
- return resolveReference(element, innerClassDepth, isMethodReference )
180
+ return resolveReference(element, innerClassDepth, isMemberReference )
138
181
.map { PsiElementResolveResult (it) }.toTypedArray()
139
182
}
140
183
@@ -154,14 +197,17 @@ object EntryPointReference : PsiReferenceProvider() {
154
197
val text = element.text.substring(range.startOffset, range.endOffset)
155
198
val parts = text.split(" ::" , limit = 2 )
156
199
157
- if (isMethodReference) {
158
- val targetMethod = newTarget as ? PsiMethod
159
- ? : throw IncorrectOperationException (" Cannot target $newTarget " )
200
+ if (isMemberReference) {
201
+ val newTargetName = when (newTarget) {
202
+ is PsiMethod -> newTarget.name
203
+ is PsiField -> newTarget.name
204
+ else -> throw IncorrectOperationException (" Cannot target $newTarget " )
205
+ }
160
206
if (parts.size == 1 ) {
161
207
throw IncorrectOperationException (" Invalid reference" )
162
208
}
163
- val methodRange = range.cutOut(TextRange .from(parts[0 ].length + 2 , parts[1 ].length))
164
- return manipulator.handleContentChange(element, methodRange, targetMethod.name )
209
+ val memberRange = range.cutOut(TextRange .from(parts[0 ].length + 2 , parts[1 ].length))
210
+ return manipulator.handleContentChange(element, memberRange, newTargetName )
165
211
} else {
166
212
val targetClass = newTarget as ? PsiClass
167
213
? : throw IncorrectOperationException (" Cannot target $newTarget " )
@@ -173,5 +219,39 @@ object EntryPointReference : PsiReferenceProvider() {
173
219
return manipulator.handleContentChange(element, classRange, targetClass.qualifiedName)
174
220
}
175
221
}
222
+
223
+ override fun getVariants (): Array <Any > {
224
+ val manipulator = element.manipulator
225
+ ? : return ArrayUtil .EMPTY_OBJECT_ARRAY
226
+
227
+ val range = manipulator.getRangeInElement(element)
228
+ val text = element.text.substring(range.startOffset, range.endOffset)
229
+ val parts = text.split(" ::" , limit = 2 )
230
+
231
+ val variants = mutableListOf<Any >()
232
+ if (! isMemberReference) {
233
+ val psiFacade = JavaPsiFacade .getInstance(element.project)
234
+ for (entrypoint in FabricConstants .ENTRYPOINTS ) {
235
+ val entrypointClass = psiFacade.findClass(entrypoint, element.resolveScope)
236
+ ? : continue
237
+ ClassInheritorsSearch .search(entrypointClass, true )
238
+ .mapNotNullTo(variants) {
239
+ val shortName = it.name ? : return @mapNotNullTo null
240
+ val fqName = it.fullQualifiedName ? : return @mapNotNullTo null
241
+ JavaLookupElementBuilder .forClass(it, fqName, true ).withPresentableText(shortName)
242
+ }
243
+ }
244
+ } else if (parts.size >= 2 ) {
245
+ val psiFacade = JavaPsiFacade .getInstance(element.project)
246
+ val className = parts[0 ].replace(' $' , ' .' )
247
+ val clazz = psiFacade.findClass(className, element.resolveScope)
248
+ if (clazz != null ) {
249
+ clazz.fields.filterTo(variants, ::isValidEntrypointField)
250
+ clazz.methods.filterTo(variants, ::isValidEntrypointMethod)
251
+ }
252
+ }
253
+
254
+ return variants.toTypedArray()
255
+ }
176
256
}
177
257
}
0 commit comments