Skip to content

Commit 775c435

Browse files
authored
Merge pull request #80 from domaframework/feature/function-parameter-class-complation
Function Parameter Block Code Completion
2 parents 180b965 + 0506e87 commit 775c435

File tree

10 files changed

+113
-9
lines changed

10 files changed

+113
-9
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class StaticDirectiveHandler(
199199
): Boolean {
200200
val clazzRef =
201201
PsiTreeUtil
202-
.getChildOfType(element.prevSibling, SqlElClass::class.java) // getStaticFieldAccessClazzRef(element) ?: return false
202+
.getChildOfType(element.prevSibling, SqlElClass::class.java)
203203
val fqdn =
204204
PsiTreeUtil.getChildrenOfTypeAsList(clazzRef, PsiElement::class.java).joinToString("") { it.text }
205205
val candidates = processor(fqdn, bindText) ?: return false

src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import org.domaframework.doma.intellij.psi.SqlElLeExpr
6464
import org.domaframework.doma.intellij.psi.SqlElLtExpr
6565
import org.domaframework.doma.intellij.psi.SqlElNeExpr
6666
import org.domaframework.doma.intellij.psi.SqlElOrExpr
67+
import org.domaframework.doma.intellij.psi.SqlElParameters
6768
import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr
6869
import org.domaframework.doma.intellij.psi.SqlTypes
6970
import org.jetbrains.kotlin.idea.base.util.module
@@ -164,11 +165,12 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
164165
return prevElms
165166
}
166167
}
168+
167169
// If the parent has field access, get its child element
168-
if (targetElement.parent is SqlElFieldAccessExpr) {
170+
if (parent is SqlElFieldAccessExpr) {
169171
blocks =
170172
PsiTreeUtil
171-
.getChildrenOfTypeAsList(targetElement.parent, PsiElement::class.java)
173+
.getChildrenOfTypeAsList(parent, PsiElement::class.java)
172174
.filter {
173175
(
174176
it is SqlElPrimaryExpr ||
@@ -178,7 +180,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
178180
}.toList()
179181
if (blocks.isEmpty()) {
180182
val parent =
181-
PsiTreeUtil.findFirstParent(targetElement.parent) {
183+
PsiTreeUtil.findFirstParent(parent) {
182184
it !is PsiDirectory &&
183185
it !is PsiFile &&
184186
it is SqlElFieldAccessExpr
@@ -192,14 +194,38 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
192194
(targetElement.startOffsetInParent) >= it.startOffsetInParent
193195
}.toList()
194196
}
197+
} else {
198+
// Completion for the first argument
199+
var parameterParent: PsiElement? =
200+
PsiTreeUtil.getParentOfType(targetElement, SqlElParameters::class.java)
201+
if (parameterParent != null) {
202+
blocks = emptyList()
203+
} else {
204+
// Completion for subsequent arguments
205+
parameterParent =
206+
targetElement.prevLeafs
207+
.takeWhile {
208+
it.isNotWhiteSpace() &&
209+
it.elementType != SqlTypes.LEFT_PAREN
210+
}.firstOrNull {
211+
PsiTreeUtil.getParentOfType(
212+
it,
213+
SqlElParameters::class.java,
214+
) != null
215+
}
216+
if (parameterParent != null) {
217+
blocks = emptyList()
218+
}
219+
}
195220
}
221+
196222
// If the element has no parent-child relationship,
197223
// create a list that also adds itself at the end.
198224
if (blocks.isEmpty()) {
199225
val prevElms =
200226
targetElement.findSelfBlocks()
201227
if (prevElms.isNotEmpty()) {
202-
return prevElms
228+
blocks = prevElms
203229
}
204230
}
205231
return blocks.sortedBy { it.textOffset }
@@ -220,7 +246,8 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
220246
elements.isEmpty() -> return
221247
else -> elements.first()
222248
}
223-
val topText = cleanString(top.text)
249+
250+
val topText = cleanString(getSearchElementText(top))
224251
val prevWord = PsiPatternUtil.getBindSearchWord(originalFile, elements.last(), " ")
225252
if (prevWord.startsWith("@") && prevWord.endsWith("@")) {
226253
val clazz = getRefClazz(top) { prevWord.replace("@", "") } ?: return
@@ -252,7 +279,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
252279
var listParamIndex = 0
253280
for (elm in elements.drop(1)) {
254281
index++
255-
val searchElm = cleanString(elm.text)
282+
val searchElm = cleanString(getSearchElementText(elm))
256283
if (searchElm.isEmpty()) {
257284
setFieldsAndMethodsCompletionResultSet(
258285
(psiParentClass.searchField(searchElm)?.toTypedArray() ?: emptyArray()),
@@ -287,6 +314,18 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
287314
}
288315
}
289316

317+
private fun getSearchElementText(elm: PsiElement): String =
318+
if (elm is SqlElPrimaryExpr || elm.elementType == SqlTypes.EL_IDENTIFIER) {
319+
elm.text
320+
} else {
321+
""
322+
}
323+
324+
/**
325+
* Retrieves the referenced class from a static field access element
326+
* and searches for a field or method matching the specified identifier name.
327+
* If no match is found, returns null.
328+
*/
290329
private fun getElementTypeByStaticFieldAccess(
291330
top: PsiElement,
292331
staticDirective: PsiElement,
@@ -295,7 +334,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
295334
val clazz =
296335
getRefClazz(top) {
297336
staticDirective.children
298-
.firstOrNull { it.elementType == SqlTypes.EL_CLASS }
337+
.firstOrNull { it is SqlElClass }
299338
?.text
300339
?: ""
301340
} ?: return null
@@ -304,6 +343,12 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
304343
?: clazz.findStaticMethod(topText)?.returnType
305344
}
306345

346+
/**
347+
* Retrieves the DAO method parameters that match the text of the top element.
348+
* If the element list contains one or fewer items,
349+
* the DAO method parameters are registered as suggestions and this method returns null.
350+
* If there are additional elements, it returns the class type of the top element.
351+
*/
307352
private fun getElementTypeByFieldAccess(
308353
originalFile: PsiFile,
309354
topText: String,

src/main/kotlin/org/domaframework/doma/intellij/extension/psi/PsiElementExtension.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@ fun PsiElement.isNotWhiteSpace(): Boolean = this !is PsiWhiteSpace
3030
fun PsiElement.findSelfBlocks(): List<PsiElement> {
3131
var elms = emptyList<PsiElement>()
3232
for (it in this.prevLeafs) {
33+
// When performing code completion within a [SqlElParameter] element,
34+
// there is a possibility that a “)” may be inserted for the final argument, so it is excluded.
3335
elms = elms.plus(it)
34-
if (!it.isNotWhiteSpace() || it.elementType == SqlTypes.AT_SIGN) break
36+
if (!it.isNotWhiteSpace() ||
37+
it.elementType == SqlTypes.AT_SIGN ||
38+
it.elementType == SqlTypes.LEFT_PAREN ||
39+
it.elementType == SqlTypes.COMMA
40+
) {
41+
break
42+
}
3543
}
3644

3745
elms

src/test/kotlin/org/domaframework/doma/intellij/complate/sql/SqlCompleteTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ class SqlCompleteTest : DomaSqlTest() {
4343
"SqlCompleteTestDao/completeDirectiveFieldInsideFor.sql",
4444
"SqlCompleteTestDao/completeConcatenationOperator.sql",
4545
"SqlCompleteTestDao/completeComparisonOperator.sql",
46+
"SqlCompleteTestDao/completeParameterFirst.sql",
47+
"SqlCompleteTestDao/completeParameterFirstProperty.sql",
48+
"SqlCompleteTestDao/completeParameterSecond.sql",
49+
"SqlCompleteTestDao/completeParameterSecondProperty.sql",
4650
)
4751
myFixture.enableInspections(SqlBindVariableValidInspector())
4852
}
@@ -227,6 +231,32 @@ class SqlCompleteTest : DomaSqlTest() {
227231
)
228232
}
229233

234+
fun testCompleteParameter() {
235+
innerDirectiveCompleteTest(
236+
"SqlCompleteTestDao/completeParameterFirst.sql",
237+
listOf("employee"),
238+
listOf("employeeId", "department", "rank", "startWith"),
239+
)
240+
241+
innerDirectiveCompleteTest(
242+
"SqlCompleteTestDao/completeParameterFirstProperty.sql",
243+
listOf("employeeId", "department", "rank"),
244+
listOf("employee"),
245+
)
246+
247+
innerDirectiveCompleteTest(
248+
"SqlCompleteTestDao/completeParameterSecond.sql",
249+
listOf("employee"),
250+
listOf("employeeId", "department", "rank", "startWith"),
251+
)
252+
253+
innerDirectiveCompleteTest(
254+
"SqlCompleteTestDao/completeParameterSecondProperty.sql",
255+
listOf("managerId"),
256+
listOf("employee", "department", "rank"),
257+
)
258+
}
259+
230260
private fun innerDirectiveCompleteTest(
231261
sqlFileName: String,
232262
expectedSuggestions: List<String>,

src/test/testData/src/main/java/doma/example/dao/SqlCompleteTestDao.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,16 @@ interface SqlCompleteTestDao {
5757
@Insert(sqlFile = true)
5858
int completeConcatenationOperator(Employee employee,Integer point);
5959

60+
@Select
61+
Employee completeParameterFirst(Employee employee);
62+
63+
@Select
64+
Employee completeParameterFirstProperty(Employee employee);
65+
66+
@Select
67+
Employee completeParameterSecond(Employee employee);
68+
69+
@Select
70+
Employee completeParameterSecondProperty(Employee employee);
71+
6072
}

src/test/testData/src/main/java/doma/example/entity/Employee.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ private String getEmployeeRank() {
2727
return rank;
2828
}
2929

30+
public Integer employeeParam(Integer p1, Integer p2) {
31+
return p1 + p2;
32+
}
3033
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
select * from employee
2+
where id = /* employee.department.startWith(<caret>) */1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select * from employee where id = /* employee.department.startWith(employee.<caret>) */1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select * from employee where id = /* employee.employeeParam(3, <caret>) */1
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
select * from employee
2+
where id = /* employee.employeeParam(employee.employeeId, employee.<caret>m) */1

0 commit comments

Comments
 (0)