Skip to content

Commit 193cede

Browse files
committed
Enable code completion inside function calls when argument parameters are already specified.
1 parent c7b3842 commit 193cede

File tree

10 files changed

+180
-63
lines changed

10 files changed

+180
-63
lines changed

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

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,22 @@ import org.domaframework.doma.intellij.common.util.PluginLoggerUtil
4646
import org.domaframework.doma.intellij.common.util.SqlCompletionUtil.createMethodLookupElement
4747
import org.domaframework.doma.intellij.extension.getJavaClazz
4848
import org.domaframework.doma.intellij.extension.psi.findNodeParent
49-
import org.domaframework.doma.intellij.extension.psi.findSelfBlocks
5049
import org.domaframework.doma.intellij.extension.psi.findStaticField
5150
import org.domaframework.doma.intellij.extension.psi.findStaticMethod
5251
import org.domaframework.doma.intellij.extension.psi.isNotWhiteSpace
52+
import org.domaframework.doma.intellij.extension.psi.isSqlElSymbol
5353
import org.domaframework.doma.intellij.extension.psi.searchParameter
5454
import org.domaframework.doma.intellij.extension.psi.searchStaticField
5555
import org.domaframework.doma.intellij.extension.psi.searchStaticMethod
56-
import org.domaframework.doma.intellij.psi.SqlElAndExpr
5756
import org.domaframework.doma.intellij.psi.SqlElClass
5857
import org.domaframework.doma.intellij.psi.SqlElElseifDirective
59-
import org.domaframework.doma.intellij.psi.SqlElEqExpr
6058
import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr
6159
import org.domaframework.doma.intellij.psi.SqlElForDirective
62-
import org.domaframework.doma.intellij.psi.SqlElGeExpr
63-
import org.domaframework.doma.intellij.psi.SqlElGtExpr
6460
import org.domaframework.doma.intellij.psi.SqlElIdExpr
6561
import org.domaframework.doma.intellij.psi.SqlElIfDirective
66-
import org.domaframework.doma.intellij.psi.SqlElLeExpr
67-
import org.domaframework.doma.intellij.psi.SqlElLtExpr
68-
import org.domaframework.doma.intellij.psi.SqlElNeExpr
69-
import org.domaframework.doma.intellij.psi.SqlElOrExpr
7062
import org.domaframework.doma.intellij.psi.SqlElParameters
7163
import org.domaframework.doma.intellij.psi.SqlTypes
64+
import kotlin.collections.emptyList
7265

7366
class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>() {
7467
override fun addCompletions(
@@ -111,13 +104,6 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
111104

112105
if (!isDirective) {
113106
// Check when performing code completion on the right side
114-
val prevElm =
115-
pos.prevLeafs.firstOrNull {
116-
it.isNotWhiteSpace() &&
117-
it.elementType != SqlTypes.DOT &&
118-
it !is PsiErrorElement
119-
}
120-
if (!pos.isNotWhiteSpace() && !isRightFactor(prevElm)) return
121107
val originalPosition = parameters.originalPosition ?: return
122108
val blockElements = getAccessElementTextBlocks(originalPosition)
123109
generateCompletionList(
@@ -141,29 +127,14 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
141127
}
142128
}
143129

144-
/**
145-
* Check to enable code completion even in the case of the right side
146-
*/
147-
private fun isRightFactor(prevElm: PsiElement?) =
148-
(
149-
prevElm is SqlElEqExpr ||
150-
prevElm?.elementType == SqlTypes.EL_IDENTIFIER ||
151-
prevElm is SqlElGeExpr ||
152-
prevElm is SqlElGtExpr ||
153-
prevElm is SqlElLeExpr ||
154-
prevElm is SqlElLtExpr ||
155-
prevElm is SqlElNeExpr ||
156-
prevElm is SqlElOrExpr ||
157-
prevElm is SqlElAndExpr ||
158-
prevElm?.elementType == SqlTypes.EL_PLUS ||
159-
prevElm?.elementType == SqlTypes.EL_MINUS ||
160-
prevElm?.elementType == SqlTypes.ASTERISK ||
161-
prevElm?.elementType == SqlTypes.SLASH ||
162-
prevElm?.elementType == SqlTypes.EL_PERCENT ||
163-
prevElm?.isNotWhiteSpace() == true
164-
)
165-
166130
private fun getAccessElementTextBlocks(targetElement: PsiElement): List<PsiElement> {
131+
if (PsiTreeUtil.prevLeaf(targetElement)?.elementType != SqlTypes.EL_IDENTIFIER &&
132+
targetElement.elementType != SqlTypes.EL_IDENTIFIER &&
133+
PsiTreeUtil.prevLeaf(targetElement)?.elementType != SqlTypes.DOT
134+
) {
135+
return emptyList()
136+
}
137+
167138
var blocks: List<PsiElement> = emptyList()
168139
// If the immediate parent is a for, if, elseif directive,
169140
// get the field access element list from its own forward element.
@@ -176,8 +147,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
176147
parent is SqlElIfDirective ||
177148
parent is SqlElElseifDirective
178149
) {
179-
val prevElms =
180-
targetElement.findSelfBlocks()
150+
val prevElms = findSelfBlocks(targetElement)
181151
if (prevElms.isNotEmpty()) {
182152
return prevElms
183153
}
@@ -258,8 +228,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
258228
// If the element has no parent-child relationship,
259229
// create a list that also adds itself at the end.
260230
if (blocks.isEmpty()) {
261-
val prevElms =
262-
targetElement.findSelfBlocks()
231+
val prevElms = findSelfBlocks(targetElement)
263232
if (prevElms.isNotEmpty()) {
264233
blocks = prevElms
265234
}
@@ -517,4 +486,30 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
517486
// When you enter here, it is the top element, so return static fields and methods.
518487
setFieldsAndMethodsCompletionResultSet(caretNextText, matchFields, matchMethod, result)
519488
}
489+
490+
// Get the list of elements before itself
491+
private fun findSelfBlocks(targetElement: PsiElement): List<PsiElement> {
492+
val elms =
493+
targetElement.prevLeafs
494+
.takeWhile {
495+
it.elementType != SqlTypes.BLOCK_COMMENT_START &&
496+
!it.isSqlElSymbol() &&
497+
it.elementType != SqlTypes.AT_SIGN &&
498+
(
499+
it.elementType != SqlTypes.LEFT_PAREN ||
500+
it.parent.elementType == SqlTypes.EL_PARAMETERS
501+
)
502+
}.toList()
503+
.plus(targetElement)
504+
.filter {
505+
it is PsiErrorElement ||
506+
it is SqlElIdExpr ||
507+
it.elementType == SqlTypes.EL_IDENTIFIER
508+
}.filter {
509+
it.isNotWhiteSpace() &&
510+
PsiTreeUtil.getParentOfType(it, SqlElParameters::class.java) == null
511+
}.sortedBy { it.textOffset }
512+
513+
return if (elms.isNotEmpty()) elms else emptyList()
514+
}
520515
}

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

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,14 @@ package org.domaframework.doma.intellij.extension.psi
1717

1818
import com.intellij.psi.PsiDirectory
1919
import com.intellij.psi.PsiElement
20-
import com.intellij.psi.PsiErrorElement
2120
import com.intellij.psi.PsiFile
2221
import com.intellij.psi.PsiWhiteSpace
2322
import com.intellij.psi.tree.IElementType
2423
import com.intellij.psi.util.elementType
25-
import com.intellij.psi.util.prevLeafs
26-
import org.domaframework.doma.intellij.psi.SqlElIdExpr
2724
import org.domaframework.doma.intellij.psi.SqlTypes
2825

2926
fun PsiElement.isNotWhiteSpace(): Boolean = this !is PsiWhiteSpace
3027

31-
// Get the list of elements before itself
32-
fun PsiElement.findSelfBlocks(): List<PsiElement> {
33-
val elms =
34-
this.prevLeafs
35-
.takeWhile {
36-
it.isNotWhiteSpace() &&
37-
it.elementType != SqlTypes.AT_SIGN &&
38-
(it.elementType != SqlTypes.LEFT_PAREN || it.parent.elementType == SqlTypes.EL_PARAMETERS) &&
39-
it.elementType != SqlTypes.COMMA
40-
}.toList()
41-
.plus(this)
42-
.filter { it is PsiErrorElement || it is SqlElIdExpr || it.elementType == SqlTypes.EL_IDENTIFIER }
43-
.filter { it.isNotWhiteSpace() }
44-
.sortedBy { it.textOffset }
45-
46-
return if (elms.isNotEmpty()) elms else emptyList()
47-
}
48-
4928
/**
5029
* Traverse a node's parent hierarchy
5130
* to find the parent of the specified element type up to the comment block element
@@ -64,3 +43,18 @@ fun PsiElement.findNodeParent(elementType: IElementType): PsiElement? {
6443
}
6544
return null
6645
}
46+
47+
fun PsiElement.isSqlElSymbol(): Boolean =
48+
this.elementType == SqlTypes.EL_PLUS ||
49+
this.elementType == SqlTypes.EL_MINUS ||
50+
this.elementType == SqlTypes.ASTERISK ||
51+
this.elementType == SqlTypes.EL_DIVIDE_EXPR ||
52+
this.elementType == SqlTypes.EL_EQ ||
53+
this.elementType == SqlTypes.EL_NE ||
54+
this.elementType == SqlTypes.EL_LE_EXPR ||
55+
this.elementType == SqlTypes.EL_LT_EXPR ||
56+
this.elementType == SqlTypes.EL_GE_EXPR ||
57+
this.elementType == SqlTypes.EL_GT_EXPR ||
58+
this.elementType == SqlTypes.EL_AND ||
59+
this.elementType == SqlTypes.EL_NOT ||
60+
this.elementType == SqlTypes.SEPARATOR

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,28 @@ class SqlCompleteTest : DomaSqlTest() {
3131
addSqlFile(
3232
"$testDaoName/completeDaoArgument.sql",
3333
"$testDaoName/completeInstancePropertyFromDaoArgumentClass.sql",
34+
"$testDaoName/completeInstancePropertyWithMethodParameter.sql",
3435
"$testDaoName/completeJavaPackageClass.sql",
3536
"$testDaoName/completeDirective.sql",
3637
"$testDaoName/completeBatchInsert.sql",
3738
"$testDaoName/completeStaticPropertyFromStaticPropertyCall.sql",
3839
"$testDaoName/completePropertyAfterStaticPropertyCall.sql",
40+
"$testDaoName/completePropertyAfterStaticPropertyCallWithMethodParameter.sql",
3941
"$testDaoName/completePropertyAfterStaticMethodCall.sql",
4042
"$testDaoName/completeBuiltinFunction.sql",
4143
"$testDaoName/completeDirectiveInsideIf.sql",
44+
"$testDaoName/completeDirectiveFieldInsideIfWithMethodParameter.sql",
4245
"$testDaoName/completeDirectiveInsideElseIf.sql",
4346
"$testDaoName/completeDirectiveInsideFor.sql",
47+
"$testDaoName/completeDirectiveInsideForWithMethodParameter.sql",
4448
"$testDaoName/completeDirectiveFieldInsideIf.sql",
4549
"$testDaoName/completeDirectiveFieldInsideElseIf.sql",
4650
"$testDaoName/completeDirectiveFieldInsideFor.sql",
4751
"$testDaoName/completeConcatenationOperator.sql",
4852
"$testDaoName/completeComparisonOperator.sql",
4953
"$testDaoName/completeParameterFirst.sql",
5054
"$testDaoName/completeParameterFirstProperty.sql",
55+
"$testDaoName/completeParameterFirstPropertyWithMethodParameter.sql",
5156
"$testDaoName/completeParameterSecond.sql",
5257
"$testDaoName/completeParameterSecondProperty.sql",
5358
"$testDaoName/completeParameterFirstInStaticAccess.sql",
@@ -106,6 +111,26 @@ class SqlCompleteTest : DomaSqlTest() {
106111
),
107112
listOf("getEmployeeRank()"),
108113
)
114+
innerDirectiveCompleteTest(
115+
"$testDaoName/completeInstancePropertyWithMethodParameter.sql",
116+
listOf(
117+
"byteValue()",
118+
"compareTo()",
119+
"doubleValue()",
120+
"toString()",
121+
),
122+
listOf(
123+
"employeeId",
124+
"employeeName",
125+
"department",
126+
"rank",
127+
"getFirstProject()",
128+
"toLowerCase()",
129+
"charAt()",
130+
"contains()",
131+
"isBlank()",
132+
),
133+
)
109134
}
110135

111136
fun testCompleteJavaPackageClass() {
@@ -212,6 +237,28 @@ class SqlCompleteTest : DomaSqlTest() {
212237
"addTermNumber()",
213238
),
214239
)
240+
241+
innerDirectiveCompleteTest(
242+
"$testDaoName/completePropertyAfterStaticPropertyCallWithMethodParameter.sql",
243+
listOf(
244+
"projectId",
245+
"projectNumber",
246+
"projectName",
247+
"projectCategory",
248+
),
249+
listOf(
250+
"projectDetailId",
251+
"members",
252+
"manager",
253+
"getFirstEmployee()",
254+
"getTermNumber()",
255+
"employeeId",
256+
"projects",
257+
"rank",
258+
"contains()",
259+
"isBlank()",
260+
),
261+
)
215262
}
216263

217264
fun testCompleteCallStaticPropertyClassPackage() {
@@ -308,16 +355,30 @@ class SqlCompleteTest : DomaSqlTest() {
308355
listOf("employee"),
309356
listOf("project"),
310357
)
358+
359+
innerDirectiveCompleteTest(
360+
"$testDaoName/completeDirectiveFieldInsideIfWithMethodParameter.sql",
361+
listOf("projectNumber", "projectCategory", "getFirstEmployee()", "getTermNumber()"),
362+
listOf("project", "employee"),
363+
)
364+
311365
innerDirectiveCompleteTest(
312366
"$testDaoName/completeDirectiveInsideElseIf.sql",
313367
listOf("employee", "project"),
314368
emptyList(),
315369
)
370+
316371
innerDirectiveCompleteTest(
317372
"$testDaoName/completeDirectiveInsideFor.sql",
318373
listOf("project"),
319374
listOf("employee", "member", "%for"),
320375
)
376+
377+
innerDirectiveCompleteTest(
378+
"$testDaoName/completeDirectiveInsideForWithMethodParameter.sql",
379+
listOf("clear()", "contains()", "get()", "getFirst()"),
380+
listOf("projectNumber", "projectCategory", "getFirstEmployee()", "getTermNumber()"),
381+
)
321382
}
322383

323384
fun testCompleteDirectiveFieldInside() {
@@ -374,6 +435,12 @@ class SqlCompleteTest : DomaSqlTest() {
374435
listOf("employee"),
375436
)
376437

438+
innerDirectiveCompleteTest(
439+
"$testDaoName/completeParameterFirstPropertyWithMethodParameter.sql",
440+
listOf("projectNumber", "projectCategory", "getFirstEmployee()"),
441+
listOf("employeeId", "employeeName", "getFirstProject()"),
442+
)
443+
377444
innerDirectiveCompleteTest(
378445
"$testDaoName/completeParameterSecond.sql",
379446
listOf("employee"),

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ interface SqlCompleteTestDao {
1919
@Select
2020
Employee completeInstancePropertyFromDaoArgumentClass(Employee employee, String name);
2121

22+
@Select
23+
Employee completeInstancePropertyWithMethodParameter(Employee employee, String name);
24+
2225
@Insert(sqlFile = true)
2326
int completeJavaPackageClass(Employee employee);
2427

@@ -34,6 +37,9 @@ interface SqlCompleteTestDao {
3437
@Select
3538
Project completePropertyAfterStaticPropertyCall();
3639

40+
@Select
41+
Project completePropertyAfterStaticPropertyCallWithMethodParameter(Employee employee);
42+
3743
@Select
3844
Project completePropertyAfterStaticMethodCall();
3945

@@ -43,12 +49,18 @@ interface SqlCompleteTestDao {
4349
@Update(sqlFile = true)
4450
int completeDirectiveInsideIf(Employee employee,Project project);
4551

52+
@Update(sqlFile = true)
53+
int completeDirectiveFieldInsideIfWithMethodParameter(Employee employee,Project project);
54+
4655
@Update(sqlFile = true)
4756
int completeDirectiveInsideElseIf(Employee employee,Project project);
4857

4958
@Update(sqlFile = true)
5059
int completeDirectiveInsideFor(Employee employee,Project project);
5160

61+
@Update(sqlFile = true)
62+
int completeDirectiveInsideForWithMethodParameter(Employee employee,List<List<Project>> projectsList);
63+
5264
@Update(sqlFile = true)
5365
int completeDirectiveFieldInsideIf(Employee employee,Project project);
5466

@@ -70,6 +82,9 @@ interface SqlCompleteTestDao {
7082
@Select
7183
Employee completeParameterFirstProperty(Employee employee);
7284

85+
@Select
86+
Employee completeParameterFirstPropertyWithMethodParameter(Employee employee, Integer index);
87+
7388
@Select
7489
Employee completeParameterSecond(Employee employee);
7590

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class ProjectDetail {
1919
public static Integr projectNumber;
2020
private static String projectName;
2121
private static String projectCategory;
22+
private static List<Project> subProjects = new ArrayList<>();
2223

2324
private static Employee manager = new Employee();
2425

@@ -44,4 +45,8 @@ public static String getCustomNumber(String prefix) {
4445
return prefix + projectName;
4546
}
4647

48+
public static Project getProject(String index){
49+
return subProjects.get(Integer.parseInt(index));
50+
}
51+
4752
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
update employee e set
2+
e.employee_name = /* employee.employeeName */'name'
3+
, e.rank = /* employee.rank */1
4+
where
5+
e.employee_id = /* employee.employeeId */1
6+
/*%if employee.getFirstProject(employee.employeeId).<caret> */
7+
and e.department = /* employee.department */'department'
8+
/*%elseif employee.department.startWith("100") */
9+
and e.sub_department = /* employee.department */'department'
10+
and e.rank >= /* employee.rank */9999
11+
/*%else */
12+
and e.sub_department = /* employee.department */'department'
13+
/*%end*/

0 commit comments

Comments
 (0)