diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt index d26a65b4..877a8295 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt @@ -20,6 +20,7 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiClassType import com.intellij.psi.PsiElement import com.intellij.psi.PsiType +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager @@ -225,7 +226,8 @@ class ForDirectiveUtil { var isBatchAnnotation = false val forItemDeclarationBlocks = if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) { - val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr + val staticFieldAccessExpr = + forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr staticFieldAccessExpr.accessElements } else { forDirectiveDeclaration.getDeclarationChildren() @@ -234,18 +236,25 @@ class ForDirectiveUtil { // Defined by StaticFieldAccess if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) { val file = topForDirectiveItem.containingFile - val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr + val staticFieldAccessExpr = + forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr val clazz = staticFieldAccessExpr.elClass val staticElement = PsiStaticElement(clazz.elIdExprList, file) val referenceClazz = staticElement.getRefClazz() ?: return null // In the case of staticFieldAccess, the property that is called first is retrieved. - fieldAccessTopParentClass = getStaticFieldAccessTopElementClassType(staticFieldAccessExpr, referenceClazz) + fieldAccessTopParentClass = + getStaticFieldAccessTopElementClassType( + staticFieldAccessExpr, + referenceClazz, + ) } else { // Defined by Dao parameter val file = topForDirectiveItem.containingFile ?: return null val daoMethod = findDaoMethod(file) ?: return null - val topElementText = forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text ?: return null + val topElementText = + forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text + ?: return null isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() val matchParam = daoMethod.findParameter(cleanString(topElementText)) @@ -450,5 +459,28 @@ class ForDirectiveUtil { } else { "" } + + fun resolveForDirectiveClassTypeIfSuffixExists( + project: Project, + searchName: String, + ): PsiType? { + if (searchName.endsWith("_has_next")) { + return PsiType.getTypeByName( + "java.lang.Boolean", + project, + GlobalSearchScope.allScope(project), + ) + } + + if (searchName.endsWith("_index")) { + return PsiType.getTypeByName( + "java.lang.Integer", + project, + GlobalSearchScope.allScope(project), + ) + } + + return null + } } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt index 7db7e5c7..2538e08c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt @@ -419,20 +419,28 @@ class SqlParameterCompletionProvider : CompletionProvider( positionText: String, result: CompletionResultSet, ): Boolean { + val searchWord = cleanString(positionText) val project = top.project val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(top) ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) ?: return false + val specifiedClassType = ForDirectiveUtil.resolveForDirectiveClassTypeIfSuffixExists(project, top.text) + val topClassType = + if (specifiedClassType != null) { + PsiParentClass(specifiedClassType) + } else { + forItemClassType + } + val result = ForDirectiveUtil.getFieldAccessLastPropertyClassType( elements, project, - forItemClassType, + topClassType, shortName = "", dropLastIndex = 1, complete = { lastType -> - val searchWord = cleanString(positionText) setFieldsAndMethodsCompletionResultSet( lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(), lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(), diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt index c665a50c..4ce0034e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/ForItemElementDocumentationProvider.kt @@ -16,23 +16,10 @@ package org.domaframework.doma.intellij.document import com.intellij.lang.documentation.AbstractDocumentationProvider -import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.common.dao.findDaoMethod -import org.domaframework.doma.intellij.common.psi.PsiDaoMethod -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.psi.PsiStaticElement -import org.domaframework.doma.intellij.common.sql.foritem.ForItem -import org.domaframework.doma.intellij.common.util.ForDirectiveUtil -import org.domaframework.doma.intellij.extension.expr.accessElements -import org.domaframework.doma.intellij.extension.expr.accessElementsPrevOriginalElement -import org.domaframework.doma.intellij.extension.psi.findParameter -import org.domaframework.doma.intellij.extension.psi.getForItem -import org.domaframework.doma.intellij.extension.psi.psiClassType -import org.domaframework.doma.intellij.psi.SqlElClass -import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr +import org.domaframework.doma.intellij.document.generator.DocumentDaoParameterGenerator +import org.domaframework.doma.intellij.document.generator.DocumentStaticFieldGenerator import org.domaframework.doma.intellij.psi.SqlElIdExpr import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes @@ -56,93 +43,26 @@ class ForItemElementDocumentationProvider : AbstractDocumentationProvider() { val staticFieldAccessExpr = PsiTreeUtil.getParentOfType(originalElement, SqlElStaticFieldAccessExpr::class.java) - if (staticFieldAccessExpr != null) { - generateStaticFieldDocument( - staticFieldAccessExpr, - file, - originalElement, - project, - result, - ) - } else { - generateDaoFieldAccessDocument(originalElement, project, result) - } - return result.joinToString("\n") - } - - private fun generateDaoFieldAccessDocument( - originalElement: PsiElement, - project: Project, - result: MutableList, - ) { - var topParentType: PsiParentClass? = null - val selfSkip = isSelfSkip(originalElement) - val forDirectives = ForDirectiveUtil.getForDirectiveBlocks(originalElement, selfSkip) - val fieldAccessExpr = - PsiTreeUtil.getParentOfType( - originalElement, - SqlElFieldAccessExpr::class.java, - ) - val fieldAccessBlocks = - fieldAccessExpr?.accessElementsPrevOriginalElement(originalElement.textOffset) - val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement - - var isBatchAnnotation = false - if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) { - topParentType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives) - } else { - val daoMethod = findDaoMethod(originalElement.containingFile) ?: return - val param = daoMethod.findParameter(originalElement.text) ?: return - isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() - topParentType = PsiParentClass(param.type) - } - if (fieldAccessExpr != null && fieldAccessBlocks != null) { - topParentType?.let { - ForDirectiveUtil.getFieldAccessLastPropertyClassType( - fieldAccessBlocks, + val generator = + if (staticFieldAccessExpr != null) { + DocumentStaticFieldGenerator( + originalElement, + project, + result, + staticFieldAccessExpr, + file, + ) + } else { + DocumentDaoParameterGenerator( + originalElement, project, - it, - isBatchAnnotation = isBatchAnnotation, - complete = { lastType -> - result.add("${generateTypeLink(lastType)} ${originalElement.text}") - }, + result, ) } - return - } - result.add("${generateTypeLink(topParentType)} ${originalElement.text}") - } - - private fun generateStaticFieldDocument( - staticFieldAccessExpr: SqlElStaticFieldAccessExpr, - file: PsiFile, - originalElement: PsiElement, - project: Project, - result: MutableList, - ) { - val fieldAccessBlocks = staticFieldAccessExpr.accessElements - val staticElement = PsiStaticElement(fieldAccessBlocks, file) - val referenceClass = staticElement.getRefClazz() ?: return - if (PsiTreeUtil.getParentOfType(originalElement, SqlElClass::class.java) != null) { - val clazzType = PsiParentClass(referenceClass.psiClassType) - result.add("${generateTypeLink(clazzType)} ${originalElement.text}") - return - } - ForDirectiveUtil.getFieldAccessLastPropertyClassType( - fieldAccessBlocks.filter { it.textOffset <= originalElement.textOffset }, - project, - PsiParentClass(referenceClass.psiClassType), - complete = { lastType -> - result.add("${generateTypeLink(lastType)} ${originalElement.text}") - }, - ) - } + generator.generateDocument() - private fun isSelfSkip(targetElement: PsiElement): Boolean { - val forItem = ForItem(targetElement) - val forDirectiveExpr = forItem.getParentForDirectiveExpr() - return !(forDirectiveExpr != null && forDirectiveExpr.getForItem()?.textOffset == targetElement.textOffset) + return result.joinToString("\n") } override fun generateHoverDoc( @@ -159,36 +79,4 @@ class ForItemElementDocumentationProvider : AbstractDocumentationProvider() { result.add(typeDocument) return result.joinToString("\n") } - - private fun generateTypeLink(parentClass: PsiParentClass?): String { - if (parentClass?.type != null) { - return generateTypeLinkFromCanonicalText(parentClass.type.canonicalText) - } - return "" - } - - private fun generateTypeLinkFromCanonicalText(canonicalText: String): String { - val regex = Regex("([a-zA-Z0-9_]+\\.)*([a-zA-Z0-9_]+)") - val result = StringBuilder() - var lastIndex = 0 - - for (match in regex.findAll(canonicalText)) { - val fullMatch = match.value - val typeName = match.groups[2]?.value ?: fullMatch - val startIndex = match.range.first - val endIndex = match.range.last + 1 - - if (lastIndex < startIndex) { - result.append(canonicalText.substring(lastIndex, startIndex)) - } - result.append("$typeName") - lastIndex = endIndex - } - - if (lastIndex < canonicalText.length) { - result.append(canonicalText.substring(lastIndex)) - } - - return result.toString() - } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt new file mode 100644 index 00000000..20b5602d --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt @@ -0,0 +1,80 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.document.generator + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.expr.accessElementsPrevOriginalElement +import org.domaframework.doma.intellij.extension.psi.findParameter +import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr + +class DocumentDaoParameterGenerator( + val originalElement: PsiElement, + val project: Project, + val result: MutableList, +) : DocumentGenerator() { + override fun generateDocument() { + var topParentType: PsiParentClass? = null + val selfSkip = isSelfSkip(originalElement) + val forDirectives = ForDirectiveUtil.getForDirectiveBlocks(originalElement, selfSkip) + val fieldAccessExpr = + PsiTreeUtil.getParentOfType( + originalElement, + SqlElFieldAccessExpr::class.java, + ) + val fieldAccessBlocks = + fieldAccessExpr?.accessElementsPrevOriginalElement(originalElement.textOffset) + val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement + + var isBatchAnnotation = false + if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) { + val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives) + val specifiedClassType = ForDirectiveUtil.resolveForDirectiveClassTypeIfSuffixExists(project, searchElement.text) + topParentType = + if (specifiedClassType != null) { + PsiParentClass(specifiedClassType) + } else { + forItemClassType + } + } else { + val daoMethod = findDaoMethod(originalElement.containingFile) ?: return + val param = daoMethod.findParameter(originalElement.text) ?: return + isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() + topParentType = PsiParentClass(param.type) + } + + if (fieldAccessExpr != null && fieldAccessBlocks != null) { + topParentType?.let { + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + fieldAccessBlocks, + project, + it, + isBatchAnnotation = isBatchAnnotation, + complete = { lastType -> + result.add("${generateTypeLink(lastType)} ${originalElement.text}") + }, + ) + } + return + } + result.add("${generateTypeLink(topParentType)} ${originalElement.text}") + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentGenerator.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentGenerator.kt new file mode 100644 index 00000000..d4ee088a --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentGenerator.kt @@ -0,0 +1,63 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.document.generator + +import com.intellij.psi.PsiElement +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.sql.foritem.ForItem +import org.domaframework.doma.intellij.extension.psi.getForItem + +abstract class DocumentGenerator { + abstract fun generateDocument() + + protected fun isSelfSkip(targetElement: PsiElement): Boolean { + val forItem = ForItem(targetElement) + val forDirectiveExpr = forItem.getParentForDirectiveExpr() + return !(forDirectiveExpr != null && forDirectiveExpr.getForItem()?.textOffset == targetElement.textOffset) + } + + protected fun generateTypeLink(parentClass: PsiParentClass?): String { + if (parentClass?.type != null) { + return generateTypeLinkFromCanonicalText(parentClass.type.canonicalText) + } + return "" + } + + private fun generateTypeLinkFromCanonicalText(canonicalText: String): String { + val regex = Regex("([a-zA-Z0-9_]+\\.)*([a-zA-Z0-9_]+)") + val result = StringBuilder() + var lastIndex = 0 + + for (match in regex.findAll(canonicalText)) { + val fullMatch = match.value + val typeName = match.groups[2]?.value ?: fullMatch + val startIndex = match.range.first + val endIndex = match.range.last + 1 + + if (lastIndex < startIndex) { + result.append(canonicalText.substring(lastIndex, startIndex)) + } + result.append("$typeName") + lastIndex = endIndex + } + + if (lastIndex < canonicalText.length) { + result.append(canonicalText.substring(lastIndex)) + } + + return result.toString() + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentStaticFieldGenerator.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentStaticFieldGenerator.kt new file mode 100644 index 00000000..b0f82b24 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentStaticFieldGenerator.kt @@ -0,0 +1,56 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.document.generator + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.expr.accessElements +import org.domaframework.doma.intellij.extension.psi.psiClassType +import org.domaframework.doma.intellij.psi.SqlElClass +import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr + +class DocumentStaticFieldGenerator( + val originalElement: PsiElement, + val project: Project, + val result: MutableList, + val staticFieldAccessExpr: SqlElStaticFieldAccessExpr, + val file: PsiFile, +) : DocumentGenerator() { + override fun generateDocument() { + val fieldAccessBlocks = staticFieldAccessExpr.accessElements + val staticElement = PsiStaticElement(fieldAccessBlocks, file) + val referenceClass = staticElement.getRefClazz() ?: return + if (PsiTreeUtil.getParentOfType(originalElement, SqlElClass::class.java) != null) { + val clazzType = PsiParentClass(referenceClass.psiClassType) + result.add("${generateTypeLink(clazzType)} ${originalElement.text}") + return + } + + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + fieldAccessBlocks.filter { it.textOffset <= originalElement.textOffset }, + project, + PsiParentClass(referenceClass.psiClassType), + complete = { lastType -> + result.add("${generateTypeLink(lastType)} ${originalElement.text}") + }, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt index 65db7c0d..7ac21ba2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt @@ -135,7 +135,13 @@ class SqlInspectionVisitor( errorHighlight(topElement, daoMethod, holder) return } - result + val specifiedClassType = + ForDirectiveUtil.resolveForDirectiveClassTypeIfSuffixExists(project, topElement.text) + if (specifiedClassType != null) { + PsiParentClass(specifiedClassType) + } else { + result + } } else { val paramType = daoMethod.findParameter(cleanString(topElement.text))?.type if (paramType == null) { diff --git a/src/test/kotlin/org/domaframework/doma/intellij/complate/sql/SqlCompleteTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/complate/sql/SqlCompleteTest.kt index 902ef574..5cc758ea 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/complate/sql/SqlCompleteTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/complate/sql/SqlCompleteTest.kt @@ -51,6 +51,8 @@ class SqlCompleteTest : DomaSqlTest() { "$testDapName/completeParameterSecondProperty.sql", "$testDapName/completeCallStaticPropertyClassPackage.sql", "$testDapName/completeCallStaticPropertyClass.sql", + "$testDapName/completeForItemHasNext.sql", + "$testDapName/completeForItemIndex.sql", ) myFixture.enableInspections(SqlBindVariableValidInspector()) } @@ -107,6 +109,35 @@ class SqlCompleteTest : DomaSqlTest() { ) } + fun testCompleteForItemHasNext() { + innerDirectiveCompleteTest( + "$testDapName/completeForItemHasNext.sql", + listOf( + "FALSE", + "TRUE", + "TYPE", + "toString()", + "booleanValue()", + ), + listOf("get()", "startsWith()", "permissions", "MAX_VALUE", "MIN_VALUE"), + ) + } + + fun testCompleteForItemIndex() { + innerDirectiveCompleteTest( + "$testDapName/completeForItemIndex.sql", + listOf( + "BYTES", + "MAX_VALUE", + "MIN_VALUE", + "SIZE", + "TYPE", + "Integer()", + ), + listOf("get()", "startsWith()", "permissions", "FALSE", "TRUE"), + ) + } + fun testCompleteDirective() { innerDirectiveCompleteTest( "$testDapName/completeDirective.sql", diff --git a/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt b/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt index c5646c1b..bfff1e99 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt @@ -35,6 +35,8 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { addSqlFile("$testPackage/$testDaoName/documentForItemElementByFieldAccess.sql") addSqlFile("$testPackage/$testDaoName/documentForItemFirstElement.sql") addSqlFile("$testPackage/$testDaoName/documentForItemStaticProperty.sql") + addSqlFile("$testPackage/$testDaoName/documentForItemHasNext.sql") + addSqlFile("$testPackage/$testDaoName/documentForItemIndex.sql") } fun testDocumentForItemDaoParam() { @@ -96,6 +98,22 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { documentationFindTextTest(sqlName, "item", result) } + fun testDocumentForItemHasNext() { + val sqlName = "documentForItemHasNext" + val result = + "Boolean item_has_next" + + documentationFindTextTest(sqlName, "item_has_next", result) + } + + fun testDocumentForItemIndex() { + val sqlName = "documentForItemIndex" + val result = + "Integer item_index" + + documentationFindTextTest(sqlName, "item_index", result) + } + fun testDocumentForItemStaticProperty() { val sqlName = "documentForItemStaticProperty" val result = diff --git a/src/test/kotlin/org/domaframework/doma/intellij/inspection/sql/ParameterDefinedTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/inspection/sql/ParameterDefinedTest.kt index a05c03e3..11979e54 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/inspection/sql/ParameterDefinedTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/inspection/sql/ParameterDefinedTest.kt @@ -37,6 +37,7 @@ class ParameterDefinedTest : DomaSqlTest() { "$testDaoName/resolveDaoArgumentOfListType.sql", "$testDaoName/bindVariableInFunctionParameters.sql", "$testDaoName/callStaticPropertyPackageName.sql", + "$testDaoName/bindVariableForItemHasNextAndIndex.sql", ) myFixture.enableInspections(SqlBindVariableValidInspector()) } @@ -61,6 +62,14 @@ class ParameterDefinedTest : DomaSqlTest() { myFixture.testHighlighting(false, false, false, sqlFile) } + fun testBindVariableForItemHasNextAndIndex() { + val sqlFile = findSqlFile("$testDaoName/bindVariableForItemHasNextAndIndex.sql") + assertNotNull("Not Found SQL File", sqlFile) + if (sqlFile == null) return + + myFixture.testHighlighting(false, false, false, sqlFile) + } + fun testAccessStaticProperty() { val sqlFile = findSqlFile("$testDaoName/accessStaticProperty.sql") assertNotNull("Not Found SQL File", sqlFile) diff --git a/src/test/testData/src/main/java/doma/example/dao/EmployeeSummaryDao.java b/src/test/testData/src/main/java/doma/example/dao/EmployeeSummaryDao.java index c8ea18fb..88337a9c 100644 --- a/src/test/testData/src/main/java/doma/example/dao/EmployeeSummaryDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/EmployeeSummaryDao.java @@ -32,4 +32,7 @@ interface EmployeeSummaryDao { @Insert(sqlFile=true) EmployeeSummary bindVariableInFunctionParameters(Employee employee, User user); + + @Select + List bindVariableForItemHasNextAndIndex(List employees); } \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/dao/SqlCompleteTestDao.java b/src/test/testData/src/main/java/doma/example/dao/SqlCompleteTestDao.java index 4ec5bccf..05f26160 100644 --- a/src/test/testData/src/main/java/doma/example/dao/SqlCompleteTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/SqlCompleteTestDao.java @@ -75,4 +75,10 @@ interface SqlCompleteTestDao { @Select Employee completeCallStaticPropertyClass(); + @Select + Principal completeForItemHasNext(Principal principal); + + @Select + Principal completeForItemIndex(Principal principal); + } \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java b/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java index d648cd3a..61913ea2 100644 --- a/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/document/DocumentTestDao.java @@ -32,4 +32,7 @@ public interface DocumentTestDao { @Select int documentForItemStaticProperty(); + @Select + int documentForItemHasNext(Principal principal); + } \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql new file mode 100644 index 00000000..f608f1a3 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql @@ -0,0 +1,26 @@ +select p.project_id + , pd.project_detail_id + , pd.project_number + , p.project_name + , e.employee_id + , e.employee_name + from project_detail pd + inner join project p + on p.project_id = pd.project_id + inner join employee e + on pd.employee_id = e.employee_id + -- Use as List-type + /*%if employees.size() > 0 */ + where + /*%for member : employees */ + /*%if member_has_next */ + /*# "or" */ + /*%end */ + p.employee_id = /* member.employeeId */0 + and p.not_next = /* member_has_next.NotTRUE */false + and p.next = /* member_has_next.TRUE */false + and p.not_index = /* member_index.nextValue() */999 + and p.index = /* member_index.MIN_VALUE */0 + /*%end */ + p.employee_id = /* employees.get(0).employeeId */0 + /*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemHasNext.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemHasNext.sql new file mode 100644 index 00000000..e7b3ec33 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemHasNext.sql @@ -0,0 +1,8 @@ +select * from principal +where +/*%for item : principal.permissions */ +index = /* item_index */0 + /*%if item_has_next */ + OR flag = /* item_has_next. */false + /*%end */ +/*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemIndex.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemIndex.sql new file mode 100644 index 00000000..6cd5603a --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeForItemIndex.sql @@ -0,0 +1,8 @@ +select * from principal +where +/*%for item : principal.permissions */ +index = /* item_index. */0 + /*%if item_has_next */ + OR flag = /* item_has_next.FALSE */false + /*%end */ +/*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemHasNext.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemHasNext.sql new file mode 100644 index 00000000..144793cc --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemHasNext.sql @@ -0,0 +1,8 @@ +select count(*) from principal +where +/*%for item : principal.permissions */ + name = /* item.name */'name' + /*%if item_has_next */ + /*# "or" */ + /*%end */ +/*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemIndex.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemIndex.sql new file mode 100644 index 00000000..cc780d5b --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/document/DocumentTestDao/documentForItemIndex.sql @@ -0,0 +1,8 @@ +select count(*) from principal +where +/*%for item : principal.permissions */ + index = /* item_index */0 + /*%if item_has_next */ + /*# "or" */ + /*%end */ +/*%end */ \ No newline at end of file