Skip to content

Commit a50237e

Browse files
committed
Implemented class package code completion for staticProperty calls
1 parent 4dd0725 commit a50237e

File tree

8 files changed

+442
-237
lines changed

8 files changed

+442
-237
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiStaticElement.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import com.intellij.psi.PsiFile
2121
import com.intellij.psi.search.GlobalSearchScope
2222
import org.domaframework.doma.intellij.extension.getJavaClazz
2323
import org.domaframework.doma.intellij.psi.SqlElExpr
24-
import org.jetbrains.kotlin.idea.base.util.module
2524

2625
/**
2726
* Directive information for static property references
@@ -31,7 +30,6 @@ class PsiStaticElement(
3130
private val originalFile: PsiFile,
3231
) {
3332
private var fqdn = elExprList?.joinToString(".") { e -> e.text } ?: ""
34-
private val module = originalFile.module
3533

3634
constructor(elExprNames: String, file: PsiFile) : this(null, file) {
3735
fqdn =
@@ -40,10 +38,12 @@ class PsiStaticElement(
4038
.substringBefore("@")
4139
}
4240

43-
fun getRefClazz(): PsiClass? =
44-
module?.getJavaClazz(true, fqdn)
41+
fun getRefClazz(): PsiClass? {
42+
val project = originalFile.project
43+
return project.getJavaClazz(fqdn)
4544
?: JavaPsiFacade.getInstance(originalFile.project).findClass(
4645
fqdn,
4746
GlobalSearchScope.allScope(originalFile.project),
4847
)
48+
}
4949
}

src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt

Lines changed: 21 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -16,300 +16,90 @@
1616
package org.domaframework.doma.intellij.common.sql.directive
1717

1818
import com.intellij.codeInsight.completion.CompletionResultSet
19-
import com.intellij.codeInsight.lookup.AutoCompletionPolicy
20-
import com.intellij.codeInsight.lookup.LookupElement
21-
import com.intellij.codeInsight.lookup.LookupElementBuilder
22-
import com.intellij.codeInsight.lookup.VariableLookupItem
19+
import com.intellij.openapi.module.Module
2320
import com.intellij.openapi.project.Project
24-
import com.intellij.openapi.vfs.VirtualFile
2521
import com.intellij.psi.PsiElement
26-
import com.intellij.psi.PsiFile
27-
import com.intellij.psi.PsiManager
28-
import com.intellij.psi.PsiType
29-
import com.intellij.psi.search.GlobalSearchScope
3022
import com.intellij.psi.util.PsiTreeUtil
3123
import com.intellij.psi.util.elementType
32-
import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType
33-
import org.domaframework.doma.intellij.common.psi.PsiParentClass
34-
import org.domaframework.doma.intellij.common.psi.PsiPatternUtil
35-
import org.domaframework.doma.intellij.common.psi.PsiStaticElement
36-
import org.domaframework.doma.intellij.common.sql.cleanString
37-
import org.domaframework.doma.intellij.extension.getContentRoot
38-
import org.domaframework.doma.intellij.extension.psi.psiClassType
24+
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticBuildFunctionCollector
25+
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticClassPackageCollector
26+
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticPropertyCollector
3927
import org.domaframework.doma.intellij.psi.SqlElClass
4028
import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
4129
import org.domaframework.doma.intellij.psi.SqlTypes
42-
import org.jetbrains.kotlin.idea.util.getSourceRoot
30+
import org.jetbrains.kotlin.idea.base.util.module
4331

4432
class StaticDirectiveHandler(
45-
private val originalFile: PsiElement,
33+
originalFile: PsiElement,
4634
private val element: PsiElement,
4735
private val result: CompletionResultSet,
4836
private val bindText: String,
4937
private val project: Project,
5038
) : DirectiveHandler(originalFile) {
51-
/**
52-
* Function information displayed with code completion for built-in functions
53-
*/
54-
data class DomaFunction(
55-
val name: String,
56-
val returnType: PsiType,
57-
val parameters: List<PsiType>,
58-
)
59-
60-
/**
61-
* Show parameters in code completion for fields and methods
62-
*/
63-
data class CompletionSuggest(
64-
val field: List<VariableLookupItem>,
65-
val methods: List<LookupElement>,
66-
)
67-
6839
override fun directiveHandle(): Boolean {
6940
var handleResult = false
7041
if (element.prevSibling is SqlElStaticFieldAccessExpr) {
71-
handleResult =
72-
staticDirectiveHandler(element, result) { fqdn, bind ->
73-
val psiStaticElement = PsiStaticElement(fqdn, originalFile.containingFile)
74-
val javaClass =
75-
psiStaticElement.getRefClazz() ?: return@staticDirectiveHandler null
76-
val parentClazz = PsiParentClass(javaClass.psiClassType)
77-
parentClazz.let { clazz ->
78-
val fields =
79-
clazz.searchStaticField(bind)?.map { f -> VariableLookupItem(f) }
80-
val methods =
81-
clazz.searchStaticMethod(bind)?.map { m ->
82-
LookupElementBuilder
83-
.create("${m.name}()")
84-
.withPresentableText(m.name)
85-
.withTailText(m.parameterList.text, true)
86-
.withTypeText(m.returnType?.presentableText ?: "")
87-
}
88-
CompletionSuggest(fields ?: emptyList(), methods ?: emptyList())
89-
}
90-
}
42+
handleResult = staticDirectiveHandler(element, result)
9143
}
9244
if (handleResult) return true
9345

9446
if (PsiTreeUtil.nextLeaf(element)?.elementType == SqlTypes.AT_SIGN ||
9547
element.elementType == SqlTypes.AT_SIGN
9648
) {
49+
val module = element.module ?: return false
9750
handleResult =
98-
staticClassPath(
51+
collectionModulePackages(
52+
module,
9953
result,
100-
) { file, root ->
101-
val rootChildren = root.children
102-
if (PsiTreeUtil.prevLeaf(element)?.elementType == SqlTypes.AT_SIGN) {
103-
return@staticClassPath rootChildren.map {
104-
LookupElementBuilder
105-
.create(it.name)
106-
.withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE)
107-
}
108-
} else {
109-
val prevPackageNames =
110-
PsiPatternUtil.getBindSearchWord(file, element, "@").split(".")
111-
val topPackage =
112-
rootChildren.firstOrNull { it.name == prevPackageNames.firstOrNull() }
113-
?: return@staticClassPath null
114-
var nextPackage: VirtualFile? =
115-
topPackage
116-
if (prevPackageNames.size > 2) {
117-
for (packageName in prevPackageNames.drop(1).dropLast(1)) {
118-
if (nextPackage == null) break
119-
nextPackage =
120-
nextPackage.children.firstOrNull {
121-
it.name == cleanString(packageName)
122-
}
123-
}
124-
}
125-
return@staticClassPath nextPackage
126-
?.children
127-
?.filter {
128-
it.name.contains(cleanString(prevPackageNames.lastOrNull() ?: ""))
129-
}?.map {
130-
val packageName = prevPackageNames.joinToString(".").plus(it.nameWithoutExtension)
131-
val suggestName =
132-
it.nameWithoutExtension
133-
if (!isJavaOrKotlinFileType(it)) {
134-
suggestName.plus(".")
135-
}
136-
LookupElementBuilder
137-
.create(packageName)
138-
.withPresentableText(suggestName)
139-
.withTailText("($packageName)", true)
140-
.withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE)
141-
}
142-
}
143-
}
54+
)
14455
}
14556
if (handleResult) return true
14657

14758
if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) {
14859
// Built-in function completion
149-
handleResult =
150-
builtInDirectiveHandler(element, result) { bind ->
151-
listOf(
152-
DomaFunction(
153-
"escape",
154-
getJavaLangString(),
155-
listOf(
156-
getPsiTypeByClassName("java.lang.CharSequence"),
157-
getPsiTypeByClassName("java.lang.Char"),
158-
),
159-
),
160-
DomaFunction(
161-
"prefix",
162-
getJavaLangString(),
163-
listOf(
164-
getPsiTypeByClassName("java.lang.CharSequence"),
165-
getPsiTypeByClassName("java.lang.Char"),
166-
),
167-
),
168-
DomaFunction(
169-
"infix",
170-
getJavaLangString(),
171-
listOf(
172-
getPsiTypeByClassName("java.lang.CharSequence"),
173-
getPsiTypeByClassName("java.lang.Char"),
174-
),
175-
),
176-
DomaFunction(
177-
"suffix",
178-
getJavaLangString(),
179-
listOf(
180-
getPsiTypeByClassName("java.lang.CharSequence"),
181-
getPsiTypeByClassName("java.lang.Char"),
182-
),
183-
),
184-
DomaFunction(
185-
"roundDownTimePart",
186-
getPsiTypeByClassName("java.util.Date"),
187-
listOf(getPsiTypeByClassName("java.util.Date")),
188-
),
189-
DomaFunction(
190-
"roundDownTimePart",
191-
getPsiTypeByClassName("java.sql.Date"),
192-
listOf(getPsiTypeByClassName("java.util.Date")),
193-
),
194-
DomaFunction(
195-
"roundDownTimePart",
196-
getPsiTypeByClassName("java.sql.Timestamp"),
197-
listOf(getPsiTypeByClassName("java.sql.Timestamp")),
198-
),
199-
DomaFunction(
200-
"roundDownTimePart",
201-
getPsiTypeByClassName("java.time.LocalDateTime"),
202-
listOf(getPsiTypeByClassName("java.time.LocalDateTime")),
203-
),
204-
DomaFunction(
205-
"roundUpTimePart",
206-
getPsiTypeByClassName("java.util.Date"),
207-
listOf(getPsiTypeByClassName("java.sql.Date")),
208-
),
209-
DomaFunction(
210-
"roundUpTimePart",
211-
getPsiTypeByClassName("java.sql.Timestamp"),
212-
listOf(getPsiTypeByClassName("java.sql.Timestamp")),
213-
),
214-
DomaFunction(
215-
"roundUpTimePart",
216-
getPsiTypeByClassName("java.time.LocalDate"),
217-
listOf(getPsiTypeByClassName("java.time.LocalDate")),
218-
),
219-
DomaFunction(
220-
"isEmpty",
221-
getPsiTypeByClassName("boolean"),
222-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
223-
),
224-
DomaFunction(
225-
"isNotEmpty",
226-
getPsiTypeByClassName("boolean"),
227-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
228-
),
229-
DomaFunction(
230-
"isBlank",
231-
getPsiTypeByClassName("boolean"),
232-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
233-
),
234-
DomaFunction(
235-
"isNotBlank",
236-
getPsiTypeByClassName("boolean"),
237-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
238-
),
239-
).filter {
240-
it.name.startsWith(bind.substringAfter("@"))
241-
}.map {
242-
LookupElementBuilder
243-
.create("${it.name}()")
244-
.withPresentableText(it.name)
245-
.withTailText(
246-
"(${
247-
it.parameters.joinToString(",") { param ->
248-
param.toString().replace("PsiType:", "")
249-
}
250-
})",
251-
true,
252-
).withTypeText(it.returnType.presentableText)
253-
}
254-
}
60+
handleResult = builtInDirectiveHandler(element, result)
25561
}
25662
return handleResult
25763
}
25864

25965
private fun staticDirectiveHandler(
26066
element: PsiElement,
26167
result: CompletionResultSet,
262-
processor: (String, String) -> CompletionSuggest?,
26368
): Boolean {
26469
val clazzRef =
26570
PsiTreeUtil
26671
.getChildOfType(element.prevSibling, SqlElClass::class.java)
26772
val fqdn =
26873
PsiTreeUtil.getChildrenOfTypeAsList(clazzRef, PsiElement::class.java).joinToString("") { it.text }
269-
val candidates = processor(fqdn, bindText) ?: return false
74+
75+
val collector = StaticPropertyCollector(element, bindText)
76+
val candidates = collector.collectCompletionSuggest(fqdn) ?: return false
27077
result.addAllElements(candidates.field)
27178
candidates.methods.map { m -> result.addElement(m) }
27279
return true
27380
}
27481

275-
private fun staticClassPath(
82+
private fun collectionModulePackages(
83+
module: Module,
27684
result: CompletionResultSet,
277-
processor: (PsiFile, VirtualFile) -> List<LookupElement>?,
27885
): Boolean {
279-
val file = originalFile.containingFile ?: return false
280-
val virtualFile = file.virtualFile ?: return false
281-
val root =
282-
project
283-
.getContentRoot(virtualFile)
284-
?.children
285-
?.firstOrNull()
286-
?.getSourceRoot(project)
287-
?: return false
288-
val candidates = processor(file, root) ?: return false
86+
val collector = StaticClassPackageCollector(element, module)
87+
val candidates = collector.collect() ?: return false
28988
result.addAllElements(candidates)
29089
return true
29190
}
29291

29392
private fun builtInDirectiveHandler(
29493
element: PsiElement,
29594
result: CompletionResultSet,
296-
processor: (String) -> List<LookupElement>?,
29795
): Boolean {
29896
if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) {
29997
val prefix = getBindSearchWord(element, bindText)
300-
val candidates = processor(prefix)
98+
val collector = StaticBuildFunctionCollector(project, prefix)
99+
val candidates = collector.collect()
301100
candidates?.let { it1 -> result.addAllElements(it1) }
302101
return true
303102
}
304103
return false
305104
}
306-
307-
private fun getJavaLangString(): PsiType =
308-
PsiType.getJavaLangString(
309-
PsiManager.getInstance(project),
310-
GlobalSearchScope.allScope(project),
311-
)
312-
313-
private fun getPsiTypeByClassName(className: String): PsiType =
314-
PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project))
315105
}

0 commit comments

Comments
 (0)