Skip to content

Commit 53cbaf9

Browse files
authored
Merge pull request #148 from domaframework/feature/completion-static-class-package
Code completion and code inspection for static property call package class names
2 parents 77b52c2 + 9837f7e commit 53cbaf9

File tree

27 files changed

+689
-221
lines changed

27 files changed

+689
-221
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ The plugin also provides quick fixes for Dao methods where the required SQL file
3636
![quickfix.png](images/quickfix.png)
3737
- Checking for Dao method arguments not used in bind variables
3838
![inspection.png](images/inspection.png)
39+
- Check the class name and package name for static property calls
40+
![inspectionPackageName.png](images/inspectionPackageName.png)
3941

4042
## Completion
4143
Adds code completion functionality to support indexing of Doma directives and bind variables
@@ -44,6 +46,8 @@ Adds code completion functionality to support indexing of Doma directives and bi
4446
![complete_bindVariables.png](images/complete_bindVariables.png)
4547
- Refer to class definition from Dao method argument type and suggest fields and methods
4648
![complete_member.png](images/cpmplete_member.png)
49+
- Provide code completion for class and package names used in static property calls.
50+
![complete_package.png](images/complete_package.png)
4751
- Suggest members defined as static in static fields and method calls
4852
- Suggest Doma directives
4953
- Directives such as Condition, Loop, Population are suggested after “%”

images/complete_package.png

14.2 KB
Loading

images/inspectionPackageName.png

15.3 KB
Loading

src/main/kotlin/org/domaframework/doma/intellij/common/FileTypeCheck.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ fun isJavaOrKotlinFileType(daoFile: PsiFile): Boolean {
4646
}
4747
}
4848

49+
fun isJavaOrKotlinFileType(file: VirtualFile): Boolean {
50+
val fileType = file.fileType
51+
return when (fileType.name) {
52+
"JAVA", "Kotlin", "CLASS" -> true
53+
else -> false
54+
}
55+
}
56+
4957
/*
5058
* Determine whether the open file is an SQL template file extension
5159
*/

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: 39 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -16,218 +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.LookupElement
20-
import com.intellij.codeInsight.lookup.LookupElementBuilder
21-
import com.intellij.codeInsight.lookup.VariableLookupItem
19+
import com.intellij.openapi.module.Module
2220
import com.intellij.openapi.project.Project
2321
import com.intellij.psi.PsiElement
24-
import com.intellij.psi.PsiManager
25-
import com.intellij.psi.PsiType
26-
import com.intellij.psi.search.GlobalSearchScope
2722
import com.intellij.psi.util.PsiTreeUtil
2823
import com.intellij.psi.util.elementType
29-
import org.domaframework.doma.intellij.common.psi.PsiParentClass
30-
import org.domaframework.doma.intellij.common.psi.PsiStaticElement
31-
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
3227
import org.domaframework.doma.intellij.psi.SqlElClass
3328
import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
3429
import org.domaframework.doma.intellij.psi.SqlTypes
30+
import org.jetbrains.kotlin.idea.base.util.module
3531

3632
class StaticDirectiveHandler(
37-
private val originalFile: PsiElement,
33+
originalFile: PsiElement,
3834
private val element: PsiElement,
3935
private val result: CompletionResultSet,
4036
private val bindText: String,
4137
private val project: Project,
4238
) : DirectiveHandler(originalFile) {
43-
/**
44-
* Function information displayed with code completion for built-in functions
45-
*/
46-
data class DomaFunction(
47-
val name: String,
48-
val returnType: PsiType,
49-
val parameters: List<PsiType>,
50-
)
51-
52-
/**
53-
* Show parameters in code completion for fields and methods
54-
*/
55-
data class CompletionSuggest(
56-
val field: List<VariableLookupItem>,
57-
val methods: List<LookupElement>,
58-
)
59-
6039
override fun directiveHandle(): Boolean {
6140
var handleResult = false
6241
if (element.prevSibling is SqlElStaticFieldAccessExpr) {
42+
handleResult = staticDirectiveHandler(element, result)
43+
}
44+
if (handleResult) return true
45+
46+
if (PsiTreeUtil.nextLeaf(element)?.elementType == SqlTypes.AT_SIGN ||
47+
element.elementType == SqlTypes.AT_SIGN
48+
) {
49+
val module = element.module ?: return false
6350
handleResult =
64-
staticDirectiveHandler(element, result) { fqdn, bind ->
65-
val psiStaticElement = PsiStaticElement(fqdn, originalFile.containingFile)
66-
val javaClass =
67-
psiStaticElement.getRefClazz() ?: return@staticDirectiveHandler null
68-
val parentClazz = PsiParentClass(javaClass.psiClassType)
69-
parentClazz.let { clazz ->
70-
val fields =
71-
clazz.searchStaticField(bind)?.map { f -> VariableLookupItem(f) }
72-
val methods =
73-
clazz.searchStaticMethod(bind)?.map { m ->
74-
LookupElementBuilder
75-
.create("${m.name}()")
76-
.withPresentableText(m.name)
77-
.withTailText(m.parameterList.text, true)
78-
.withTypeText(m.returnType?.presentableText ?: "")
79-
}
80-
CompletionSuggest(fields ?: emptyList(), methods ?: emptyList())
81-
}
82-
}
83-
} else if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) {
51+
collectionModulePackages(
52+
module,
53+
result,
54+
)
55+
}
56+
if (handleResult) return true
57+
58+
if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) {
8459
// Built-in function completion
85-
handleResult =
86-
builtInDirectiveHandler(element, result) { bind ->
87-
listOf(
88-
DomaFunction(
89-
"escape",
90-
getJavaLangString(),
91-
listOf(
92-
getPsiTypeByClassName("java.lang.CharSequence"),
93-
getPsiTypeByClassName("java.lang.Char"),
94-
),
95-
),
96-
DomaFunction(
97-
"prefix",
98-
getJavaLangString(),
99-
listOf(
100-
getPsiTypeByClassName("java.lang.CharSequence"),
101-
getPsiTypeByClassName("java.lang.Char"),
102-
),
103-
),
104-
DomaFunction(
105-
"infix",
106-
getJavaLangString(),
107-
listOf(
108-
getPsiTypeByClassName("java.lang.CharSequence"),
109-
getPsiTypeByClassName("java.lang.Char"),
110-
),
111-
),
112-
DomaFunction(
113-
"suffix",
114-
getJavaLangString(),
115-
listOf(
116-
getPsiTypeByClassName("java.lang.CharSequence"),
117-
getPsiTypeByClassName("java.lang.Char"),
118-
),
119-
),
120-
DomaFunction(
121-
"roundDownTimePart",
122-
getPsiTypeByClassName("java.util.Date"),
123-
listOf(getPsiTypeByClassName("java.util.Date")),
124-
),
125-
DomaFunction(
126-
"roundDownTimePart",
127-
getPsiTypeByClassName("java.sql.Date"),
128-
listOf(getPsiTypeByClassName("java.util.Date")),
129-
),
130-
DomaFunction(
131-
"roundDownTimePart",
132-
getPsiTypeByClassName("java.sql.Timestamp"),
133-
listOf(getPsiTypeByClassName("java.sql.Timestamp")),
134-
),
135-
DomaFunction(
136-
"roundDownTimePart",
137-
getPsiTypeByClassName("java.time.LocalDateTime"),
138-
listOf(getPsiTypeByClassName("java.time.LocalDateTime")),
139-
),
140-
DomaFunction(
141-
"roundUpTimePart",
142-
getPsiTypeByClassName("java.util.Date"),
143-
listOf(getPsiTypeByClassName("java.sql.Date")),
144-
),
145-
DomaFunction(
146-
"roundUpTimePart",
147-
getPsiTypeByClassName("java.sql.Timestamp"),
148-
listOf(getPsiTypeByClassName("java.sql.Timestamp")),
149-
),
150-
DomaFunction(
151-
"roundUpTimePart",
152-
getPsiTypeByClassName("java.time.LocalDate"),
153-
listOf(getPsiTypeByClassName("java.time.LocalDate")),
154-
),
155-
DomaFunction(
156-
"isEmpty",
157-
getPsiTypeByClassName("boolean"),
158-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
159-
),
160-
DomaFunction(
161-
"isNotEmpty",
162-
getPsiTypeByClassName("boolean"),
163-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
164-
),
165-
DomaFunction(
166-
"isBlank",
167-
getPsiTypeByClassName("boolean"),
168-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
169-
),
170-
DomaFunction(
171-
"isNotBlank",
172-
getPsiTypeByClassName("boolean"),
173-
listOf(getPsiTypeByClassName("java.lang.CharSequence")),
174-
),
175-
).filter {
176-
it.name.startsWith(bind.substringAfter("@"))
177-
}.map {
178-
LookupElementBuilder
179-
.create("${it.name}()")
180-
.withPresentableText(it.name)
181-
.withTailText(
182-
"(${
183-
it.parameters.joinToString(",") { param ->
184-
param.toString().replace("PsiType:", "")
185-
}
186-
})",
187-
true,
188-
).withTypeText(it.returnType.presentableText)
189-
}
190-
}
60+
handleResult = builtInDirectiveHandler(element, result)
19161
}
19262
return handleResult
19363
}
19464

19565
private fun staticDirectiveHandler(
19666
element: PsiElement,
19767
result: CompletionResultSet,
198-
processor: (String, String) -> CompletionSuggest?,
19968
): Boolean {
20069
val clazzRef =
20170
PsiTreeUtil
20271
.getChildOfType(element.prevSibling, SqlElClass::class.java)
20372
val fqdn =
20473
PsiTreeUtil.getChildrenOfTypeAsList(clazzRef, PsiElement::class.java).joinToString("") { it.text }
205-
val candidates = processor(fqdn, bindText) ?: return false
74+
75+
val collector = StaticPropertyCollector(element, bindText)
76+
val candidates = collector.collectCompletionSuggest(fqdn) ?: return false
20677
result.addAllElements(candidates.field)
20778
candidates.methods.map { m -> result.addElement(m) }
20879
return true
20980
}
21081

82+
private fun collectionModulePackages(
83+
module: Module,
84+
result: CompletionResultSet,
85+
): Boolean {
86+
val collector = StaticClassPackageCollector(element, module)
87+
val candidates = collector.collect() ?: return false
88+
result.addAllElements(candidates)
89+
return true
90+
}
91+
21192
private fun builtInDirectiveHandler(
21293
element: PsiElement,
21394
result: CompletionResultSet,
214-
processor: (String) -> List<LookupElement>?,
21595
): Boolean {
21696
if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) {
217-
val prefix = getBindSearchWord(element, "@")
218-
val candidates = processor(prefix)
97+
val prefix = getBindSearchWord(element, bindText)
98+
val collector = StaticBuildFunctionCollector(project, prefix)
99+
val candidates = collector.collect()
219100
candidates?.let { it1 -> result.addAllElements(it1) }
220101
return true
221102
}
222103
return false
223104
}
224-
225-
private fun getJavaLangString(): PsiType =
226-
PsiType.getJavaLangString(
227-
PsiManager.getInstance(project),
228-
GlobalSearchScope.allScope(project),
229-
)
230-
231-
private fun getPsiTypeByClassName(className: String): PsiType =
232-
PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project))
233105
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.sql.directive
17+
18+
import com.intellij.codeInsight.lookup.LookupElement
19+
import com.intellij.codeInsight.lookup.VariableLookupItem
20+
import com.intellij.icons.AllIcons
21+
import com.intellij.psi.PsiType
22+
23+
/**
24+
* Function information displayed with code completion for built-in functions
25+
*/
26+
data class DomaFunction(
27+
val name: String,
28+
val returnType: PsiType,
29+
val parameters: List<PsiType>,
30+
)
31+
32+
/**
33+
* Show parameters in code completion for fields and methods
34+
*/
35+
data class CompletionSuggest(
36+
val field: List<VariableLookupItem>,
37+
val methods: List<LookupElement>,
38+
)
39+
40+
data class StaticClassPackageSearchResult(
41+
val packageName: String,
42+
val qualifiedName: String,
43+
val createText: String,
44+
val fileType: String,
45+
)
46+
47+
val ICON_MAP =
48+
mapOf(
49+
"enum" to AllIcons.Nodes.Enum,
50+
"annotation" to AllIcons.Nodes.Annotationtype,
51+
"interface" to AllIcons.Nodes.Interface,
52+
"record" to AllIcons.Nodes.Record,
53+
"package" to AllIcons.Nodes.Package,
54+
"JAVA" to AllIcons.FileTypes.Java,
55+
"CLASS" to AllIcons.Nodes.Class,
56+
)

0 commit comments

Comments
 (0)