Skip to content

Commit dd18e42

Browse files
authored
Merge pull request #215 from domaframework/fix/code-completion-fails
Code completion fails after existing values in method arguments
2 parents 16b529d + fe4d8c4 commit dd18e42

File tree

10 files changed

+179
-63
lines changed

10 files changed

+179
-63
lines changed

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

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,21 @@ 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
5352
import org.domaframework.doma.intellij.extension.psi.searchParameter
5453
import org.domaframework.doma.intellij.extension.psi.searchStaticField
5554
import org.domaframework.doma.intellij.extension.psi.searchStaticMethod
56-
import org.domaframework.doma.intellij.psi.SqlElAndExpr
5755
import org.domaframework.doma.intellij.psi.SqlElClass
5856
import org.domaframework.doma.intellij.psi.SqlElElseifDirective
59-
import org.domaframework.doma.intellij.psi.SqlElEqExpr
6057
import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr
6158
import org.domaframework.doma.intellij.psi.SqlElForDirective
62-
import org.domaframework.doma.intellij.psi.SqlElGeExpr
63-
import org.domaframework.doma.intellij.psi.SqlElGtExpr
6459
import org.domaframework.doma.intellij.psi.SqlElIdExpr
6560
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
7061
import org.domaframework.doma.intellij.psi.SqlElParameters
7162
import org.domaframework.doma.intellij.psi.SqlTypes
63+
import kotlin.collections.emptyList
7264

7365
class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>() {
7466
override fun addCompletions(
@@ -111,13 +103,6 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
111103

112104
if (!isDirective) {
113105
// 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
121106
val originalPosition = parameters.originalPosition ?: return
122107
val blockElements = getAccessElementTextBlocks(originalPosition)
123108
generateCompletionList(
@@ -141,29 +126,14 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
141126
}
142127
}
143128

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-
166129
private fun getAccessElementTextBlocks(targetElement: PsiElement): List<PsiElement> {
130+
if (PsiTreeUtil.prevLeaf(targetElement)?.elementType != SqlTypes.EL_IDENTIFIER &&
131+
targetElement.elementType != SqlTypes.EL_IDENTIFIER &&
132+
PsiTreeUtil.prevLeaf(targetElement)?.elementType != SqlTypes.DOT
133+
) {
134+
return emptyList()
135+
}
136+
167137
var blocks: List<PsiElement> = emptyList()
168138
// If the immediate parent is a for, if, elseif directive,
169139
// get the field access element list from its own forward element.
@@ -176,8 +146,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
176146
parent is SqlElIfDirective ||
177147
parent is SqlElElseifDirective
178148
) {
179-
val prevElms =
180-
targetElement.findSelfBlocks()
149+
val prevElms = findSelfBlocks(targetElement)
181150
if (prevElms.isNotEmpty()) {
182151
return prevElms
183152
}
@@ -258,8 +227,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
258227
// If the element has no parent-child relationship,
259228
// create a list that also adds itself at the end.
260229
if (blocks.isEmpty()) {
261-
val prevElms =
262-
targetElement.findSelfBlocks()
230+
val prevElms = findSelfBlocks(targetElement)
263231
if (prevElms.isNotEmpty()) {
264232
blocks = prevElms
265233
}
@@ -517,4 +485,45 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
517485
// When you enter here, it is the top element, so return static fields and methods.
518486
setFieldsAndMethodsCompletionResultSet(caretNextText, matchFields, matchMethod, result)
519487
}
488+
489+
// Get the list of elements before itself
490+
private fun findSelfBlocks(targetElement: PsiElement): List<PsiElement> {
491+
val elms =
492+
targetElement.prevLeafs
493+
.takeWhile {
494+
it.elementType != SqlTypes.BLOCK_COMMENT_START &&
495+
!isSqlElSymbol(it) &&
496+
it.elementType != SqlTypes.AT_SIGN &&
497+
(
498+
it.elementType != SqlTypes.LEFT_PAREN ||
499+
it.parent.elementType == SqlTypes.EL_PARAMETERS
500+
)
501+
}.toList()
502+
.plus(targetElement)
503+
.filter {
504+
it is PsiErrorElement ||
505+
it is SqlElIdExpr ||
506+
it.elementType == SqlTypes.EL_IDENTIFIER
507+
}.filter {
508+
it.isNotWhiteSpace() &&
509+
PsiTreeUtil.getParentOfType(it, SqlElParameters::class.java) == null
510+
}.sortedBy { it.textOffset }
511+
512+
return if (elms.isNotEmpty()) elms else emptyList()
513+
}
514+
515+
private fun isSqlElSymbol(element: PsiElement): Boolean =
516+
element.elementType == SqlTypes.EL_PLUS ||
517+
element.elementType == SqlTypes.EL_MINUS ||
518+
element.elementType == SqlTypes.ASTERISK ||
519+
element.elementType == SqlTypes.EL_DIVIDE_EXPR ||
520+
element.elementType == SqlTypes.EL_EQ ||
521+
element.elementType == SqlTypes.EL_NE ||
522+
element.elementType == SqlTypes.EL_LE_EXPR ||
523+
element.elementType == SqlTypes.EL_LT_EXPR ||
524+
element.elementType == SqlTypes.EL_GE_EXPR ||
525+
element.elementType == SqlTypes.EL_GT_EXPR ||
526+
element.elementType == SqlTypes.EL_AND ||
527+
element.elementType == SqlTypes.EL_NOT ||
528+
element.elementType == SqlTypes.SEPARATOR
520529
}

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

Lines changed: 0 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

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)