Skip to content

Commit 0eeee52

Browse files
committed
Prevent duplicate argument parameters in function code completion
1 parent 800f813 commit 0eeee52

File tree

10 files changed

+90
-33
lines changed

10 files changed

+90
-33
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class DirectiveCompletion(
2323
private val originalFile: PsiFile,
2424
private val bindText: String,
2525
private val element: PsiElement,
26+
private val caretNextText: String,
2627
private val result: CompletionResultSet,
2728
) {
2829
fun directiveHandle(symbol: String): Boolean {
@@ -52,6 +53,7 @@ class DirectiveCompletion(
5253
StaticDirectiveHandler(
5354
originalFile = originalFile,
5455
element = element,
56+
caretNextText = caretNextText,
5557
result = result,
5658
bindText = bindText,
5759
).directiveHandle()

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.intellij.openapi.module.Module
2020
import com.intellij.psi.PsiElement
2121
import com.intellij.psi.util.PsiTreeUtil
2222
import com.intellij.psi.util.elementType
23-
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticBuildFunctionCollector
23+
import org.domaframework.doma.intellij.common.sql.directive.collector.FunctionCallCollector
2424
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticClassPackageCollector
2525
import org.domaframework.doma.intellij.common.sql.directive.collector.StaticPropertyCollector
2626
import org.domaframework.doma.intellij.psi.SqlElClass
@@ -31,12 +31,14 @@ import org.jetbrains.kotlin.idea.base.util.module
3131
class StaticDirectiveHandler(
3232
originalFile: PsiElement,
3333
private val element: PsiElement,
34+
private val caretNextText: String,
3435
private val result: CompletionResultSet,
3536
private val bindText: String,
3637
) : DirectiveHandler(originalFile) {
3738
override fun directiveHandle(): Boolean {
3839
var handleResult = false
39-
if (element.prevSibling is SqlElStaticFieldAccessExpr) {
40+
41+
if (isNextStaticFieldAccess(element)) {
4042
handleResult = staticDirectiveHandler(element, result)
4143
}
4244
if (handleResult) return true
@@ -60,20 +62,30 @@ class StaticDirectiveHandler(
6062
return handleResult
6163
}
6264

65+
private fun isNextStaticFieldAccess(element: PsiElement): Boolean {
66+
val prev = PsiTreeUtil.prevLeaf(element)
67+
return element.prevSibling is SqlElStaticFieldAccessExpr ||
68+
(
69+
prev?.elementType == SqlTypes.AT_SIGN &&
70+
prev.parent is SqlElStaticFieldAccessExpr
71+
)
72+
}
73+
6374
private fun staticDirectiveHandler(
6475
element: PsiElement,
6576
result: CompletionResultSet,
6677
): Boolean {
6778
val clazzRef =
6879
PsiTreeUtil
6980
.getChildOfType(element.prevSibling, SqlElClass::class.java)
81+
?: PsiTreeUtil.getChildOfType(PsiTreeUtil.prevLeaf(element)?.parent, SqlElClass::class.java)
7082
val fqdn =
7183
PsiTreeUtil.getChildrenOfTypeAsList(clazzRef, PsiElement::class.java).joinToString("") { it.text }
7284

73-
val collector = StaticPropertyCollector(element, bindText)
85+
val collector = StaticPropertyCollector(element, caretNextText, bindText)
7486
val candidates = collector.collectCompletionSuggest(fqdn) ?: return false
7587
result.addAllElements(candidates.field)
76-
candidates.methods.map { m -> result.addElement(m) }
88+
candidates.methods.forEach { m -> result.addElement(m) }
7789
return true
7890
}
7991

@@ -93,7 +105,8 @@ class StaticDirectiveHandler(
93105
): Boolean {
94106
if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) {
95107
val prefix = getBindSearchWord(element, bindText)
96-
val collector = StaticBuildFunctionCollector(element.containingFile, prefix)
108+
val collector =
109+
FunctionCallCollector(element.containingFile, caretNextText, prefix)
97110
val candidates = collector.collect()
98111
candidates?.let { it1 -> result.addAllElements(it1) }
99112
return true

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,6 @@ package org.domaframework.doma.intellij.common.sql.directive
1818
import com.intellij.codeInsight.lookup.LookupElement
1919
import com.intellij.codeInsight.lookup.VariableLookupItem
2020
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-
)
3121

3222
/**
3323
* Show parameters in code completion for fields and methods
Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import com.intellij.psi.PsiModifier
2424
import org.domaframework.doma.intellij.common.CommonPathParameterUtil
2525
import org.domaframework.doma.intellij.common.config.DomaCompileConfigUtil
2626
import org.domaframework.doma.intellij.common.helper.ExpressionFunctionsHelper
27+
import org.domaframework.doma.intellij.common.util.SqlCompletionUtil.createMethodLookupElement
2728
import org.domaframework.doma.intellij.extension.getJavaClazz
2829
import org.jetbrains.kotlin.idea.base.util.module
2930

30-
class StaticBuildFunctionCollector(
31+
class FunctionCallCollector(
3132
private val file: PsiFile?,
33+
private val caretNextText: String,
3234
private val bind: String,
3335
) : StaticDirectiveHandlerCollector() {
3436
public override fun collect(): List<LookupElement>? {
@@ -44,7 +46,13 @@ class StaticBuildFunctionCollector(
4446
}
4547

4648
val customFunctionClassName =
47-
project?.let { DomaCompileConfigUtil.getConfigValue(it, resourcePaths, "doma.expr.functions") }
49+
project?.let {
50+
DomaCompileConfigUtil.getConfigValue(
51+
it,
52+
resourcePaths,
53+
"doma.expr.functions",
54+
)
55+
}
4856

4957
val expressionFunctionInterface =
5058
project?.let { ExpressionFunctionsHelper.setExpressionFunctionsInterface(it) }
@@ -62,21 +70,19 @@ class StaticBuildFunctionCollector(
6270
)
6371
}
6472

65-
if (functions.isEmpty()) {
66-
functions.addAll(
67-
expressionFunctionInterface.allMethods.filter {
68-
isPublicFunction(it)
69-
},
70-
)
71-
}
73+
functions.addAll(
74+
expressionFunctionInterface.allMethods.filter {
75+
isPublicFunction(it) && !functions.contains(it)
76+
},
77+
)
7278

7379
return functions
7480
.filter {
7581
it.name.startsWith(bind.substringAfter("@"))
7682
}.map {
7783
val parameters = it.parameterList.parameters.toList()
7884
LookupElementBuilder
79-
.create("${it.name}()")
85+
.create(createMethodLookupElement(caretNextText, it))
8086
.withPresentableText(it.name)
8187
.withTailText(
8288
"(${

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ import com.intellij.psi.PsiElement
2222
import org.domaframework.doma.intellij.common.psi.PsiParentClass
2323
import org.domaframework.doma.intellij.common.psi.PsiStaticElement
2424
import org.domaframework.doma.intellij.common.sql.directive.CompletionSuggest
25+
import org.domaframework.doma.intellij.common.util.SqlCompletionUtil.createMethodLookupElement
2526
import org.domaframework.doma.intellij.extension.psi.psiClassType
2627

2728
class StaticPropertyCollector(
2829
private val element: PsiElement,
30+
private val caretNextText: String,
2931
private val bind: String,
3032
) : StaticDirectiveHandlerCollector() {
3133
public override fun collectCompletionSuggest(fqdn: String): CompletionSuggest? {
@@ -39,7 +41,7 @@ class StaticPropertyCollector(
3941
val methods =
4042
clazz.searchStaticMethod(bind)?.map { m ->
4143
LookupElementBuilder
42-
.create("${m.name}()")
44+
.create(createMethodLookupElement(caretNextText, m))
4345
.withPresentableText(m.name)
4446
.withTailText(m.parameterList.text, true)
4547
.withIcon(AllIcons.Nodes.Method)

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import org.domaframework.doma.intellij.common.sql.directive.DirectiveCompletion
4343
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult
4444
import org.domaframework.doma.intellij.common.util.ForDirectiveUtil
4545
import org.domaframework.doma.intellij.common.util.PluginLoggerUtil
46+
import org.domaframework.doma.intellij.common.util.SqlCompletionUtil.createMethodLookupElement
4647
import org.domaframework.doma.intellij.extension.getJavaClazz
4748
import org.domaframework.doma.intellij.extension.psi.findNodeParent
4849
import org.domaframework.doma.intellij.extension.psi.findSelfBlocks
@@ -78,6 +79,11 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
7879
val startTime = System.nanoTime()
7980

8081
var isDirective = false
82+
val offset = parameters.editor.caretModel.currentCaret.offset
83+
val range =
84+
com.intellij.openapi.util
85+
.TextRange(offset, offset + 1)
86+
val caretNextText = parameters.editor.document.getText(range)
8187
try {
8288
val originalFile = parameters.originalFile
8389
val pos = parameters.originalPosition ?: return
@@ -86,7 +92,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
8692
.substringAfter("/*")
8793
.substringBefore("*/")
8894

89-
val handler = DirectiveCompletion(originalFile, bindText, pos, result)
95+
val handler = DirectiveCompletion(originalFile, bindText, pos, caretNextText, result)
9096
val directiveSymbols = listOf("%", "#", "^", "@")
9197
directiveSymbols.forEach {
9298
if (!isDirective) {
@@ -118,6 +124,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
118124
blockElements,
119125
pos,
120126
originalFile,
127+
caretNextText,
121128
result,
122129
)
123130
PluginLoggerUtil.countLogging(
@@ -268,6 +275,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
268275
elements: List<PsiElement>,
269276
position: PsiElement,
270277
originalFile: PsiFile,
278+
caretNextText: String,
271279
result: CompletionResultSet,
272280
) {
273281
val daoMethod = findDaoMethod(originalFile)
@@ -281,7 +289,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
281289
val topText = cleanString(getSearchElementText(top))
282290
val prevWord = PsiPatternUtil.getBindSearchWord(originalFile, elements.last(), " ")
283291
if (prevWord.startsWith("@") && prevWord.endsWith("@")) {
284-
setCompletionStaticFieldAccess(top, prevWord, topText, result)
292+
setCompletionStaticFieldAccess(top, prevWord, caretNextText, topText, result)
285293
return
286294
}
287295

@@ -298,7 +306,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
298306
val psiDaoMethod = PsiDaoMethod(project, daoMethod)
299307
if (topElementType == null) {
300308
isBatchAnnotation = psiDaoMethod.daoType.isBatchAnnotation()
301-
if (isFieldAccessByForItem(top, elements, searchText, isBatchAnnotation, result)) return
309+
if (isFieldAccessByForItem(top, elements, searchText, caretNextText, isBatchAnnotation, result)) return
302310
topElementType =
303311
getElementTypeByFieldAccess(originalFile, position, elements, daoMethod, result) ?: return
304312
}
@@ -309,6 +317,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
309317
isBatchAnnotation,
310318
elements,
311319
searchText,
320+
caretNextText,
312321
result,
313322
)
314323
}
@@ -399,6 +408,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
399408
): PsiClass? = top.project.getJavaClazz(fqdnGetter())
400409

401410
private fun setFieldsAndMethodsCompletionResultSet(
411+
caretNextText: String,
402412
fields: Array<PsiField>,
403413
methods: Array<PsiMethod>,
404414
result: CompletionResultSet,
@@ -407,7 +417,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
407417
methods.forEach { method ->
408418
val lookupElm =
409419
LookupElementBuilder
410-
.create("${method.name}()")
420+
.create(createMethodLookupElement(caretNextText, method))
411421
.withPresentableText(method.name)
412422
.withTailText(method.parameterList.text, true)
413423
.withTypeText(method.returnType?.presentableText ?: "")
@@ -422,6 +432,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
422432
top: PsiElement,
423433
elements: List<PsiElement>,
424434
searchWord: String,
435+
caretNextText: String,
425436
isBatchAnnotation: Boolean = false,
426437
result: CompletionResultSet,
427438
): Boolean {
@@ -448,6 +459,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
448459
dropLastIndex = 1,
449460
complete = { lastType ->
450461
setFieldsAndMethodsCompletionResultSet(
462+
caretNextText,
451463
lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(),
452464
lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(),
453465
result,
@@ -463,6 +475,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
463475
isBatchAnnotation: Boolean,
464476
elements: List<PsiElement>,
465477
searchWord: String,
478+
caretNextText: String,
466479
result: CompletionResultSet,
467480
) {
468481
var psiParentClass = PsiParentClass(topElementType)
@@ -477,6 +490,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
477490
dropLastIndex = 1,
478491
complete = { lastType ->
479492
setFieldsAndMethodsCompletionResultSet(
493+
caretNextText,
480494
lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(),
481495
lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(),
482496
result,
@@ -488,6 +502,7 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
488502
private fun setCompletionStaticFieldAccess(
489503
top: PsiElement,
490504
prevWord: String,
505+
caretNextText: String,
491506
topText: String,
492507
result: CompletionResultSet,
493508
) {
@@ -496,6 +511,6 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
496511
val matchMethod = clazz.searchStaticMethod(topText)
497512

498513
// When you enter here, it is the top element, so return static fields and methods.
499-
setFieldsAndMethodsCompletionResultSet(matchFields, matchMethod, result)
514+
setFieldsAndMethodsCompletionResultSet(caretNextText, matchFields, matchMethod, result)
500515
}
501516
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class SqlCompleteTest : DomaSqlTest() {
3636
"$testDaoName/completeBatchInsert.sql",
3737
"$testDaoName/completeStaticPropertyFromStaticPropertyCall.sql",
3838
"$testDaoName/completePropertyAfterStaticPropertyCall.sql",
39+
"$testDaoName/completePropertyAfterStaticMethodCall.sql",
3940
"$testDaoName/completeBuiltinFunction.sql",
4041
"$testDaoName/completeDirectiveInsideIf.sql",
4142
"$testDaoName/completeDirectiveInsideElseIf.sql",
@@ -255,6 +256,20 @@ class SqlCompleteTest : DomaSqlTest() {
255256
)
256257
}
257258

259+
fun testCompletePropertyAfterStaticMethodCall() {
260+
innerDirectiveCompleteTest(
261+
"$testDaoName/completePropertyAfterStaticMethodCall.sql",
262+
listOf(
263+
"getTermNumber()",
264+
),
265+
listOf(
266+
"managerId",
267+
"userId",
268+
"employeeName",
269+
),
270+
)
271+
}
272+
258273
fun testCompleteBuiltinFunction() {
259274
innerDirectiveCompleteTest(
260275
"$testDaoName/completeBuiltinFunction.sql",
@@ -408,7 +423,7 @@ class SqlCompleteTest : DomaSqlTest() {
408423
addResourceCompileFile("doma.compile.config")
409424
innerDirectiveCompleteTest(
410425
"$testDaoName/completeImplementCustomFunction.sql",
411-
listOf("userId()", "userName()", "userAge()"),
426+
listOf("userId", "userName", "userAge"),
412427
listOf(
413428
"getId()",
414429
"getName()",

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ interface SqlCompleteTestDao {
3434
@Select
3535
Project completePropertyAfterStaticPropertyCall();
3636

37+
@Select
38+
Project completePropertyAfterStaticMethodCall();
39+
3740
@Select
3841
Project completeBuiltinFunction(ProjectDetail detail);
3942

src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeBatchInsert.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ INSERT INTO employee
22
(id
33
, name)
44
VALUES ( /* employees.userId */0
5-
, /* employees.<caret>emplo */'name')
5+
, /* employees.getFirst<caret>Project() */'name')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
select
2+
p.project_id
3+
, p.statis
4+
, p.project_name
5+
, p.rank
6+
from project p
7+
inner join project_detail pd
8+
on p.project_id = pd.project_id
9+
where
10+
-- Code completion for static fields and methods
11+
and pd.manager_id = /* @doma.example.entity.ProjectDetail@getTerm<caret>Number() */'TODO'

0 commit comments

Comments
 (0)