diff --git a/README.md b/README.md index ce7b5f91..66b00eac 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ The plugin also provides quick fixes for Dao methods where the required SQL file ![quickfix.png](images/quickfix.png) - Checking for Dao method arguments not used in bind variables ![inspection.png](images/inspection.png) +- Check the class name and package name for static property calls + ![inspectionPackageName.png](images/inspectionPackageName.png) ## Completion Adds code completion functionality to support indexing of Doma directives and bind variables @@ -44,6 +46,8 @@ Adds code completion functionality to support indexing of Doma directives and bi ![complete_bindVariables.png](images/complete_bindVariables.png) - Refer to class definition from Dao method argument type and suggest fields and methods ![complete_member.png](images/cpmplete_member.png) +- Provide code completion for class and package names used in static property calls. + ![complete_package.png](images/complete_package.png) - Suggest members defined as static in static fields and method calls - Suggest Doma directives - Directives such as Condition, Loop, Population are suggested after “%” diff --git a/images/complete_package.png b/images/complete_package.png new file mode 100644 index 00000000..e2f27d63 Binary files /dev/null and b/images/complete_package.png differ diff --git a/images/inspectionPackageName.png b/images/inspectionPackageName.png new file mode 100644 index 00000000..2feb2615 Binary files /dev/null and b/images/inspectionPackageName.png differ diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/FileTypeCheck.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/FileTypeCheck.kt index d47bd05d..aa8d7ce8 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/FileTypeCheck.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/FileTypeCheck.kt @@ -46,6 +46,14 @@ fun isJavaOrKotlinFileType(daoFile: PsiFile): Boolean { } } +fun isJavaOrKotlinFileType(file: VirtualFile): Boolean { + val fileType = file.fileType + return when (fileType.name) { + "JAVA", "Kotlin", "CLASS" -> true + else -> false + } +} + /* * Determine whether the open file is an SQL template file extension */ diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiStaticElement.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiStaticElement.kt index e5318089..1f0e38a4 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiStaticElement.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiStaticElement.kt @@ -21,7 +21,6 @@ import com.intellij.psi.PsiFile import com.intellij.psi.search.GlobalSearchScope import org.domaframework.doma.intellij.extension.getJavaClazz import org.domaframework.doma.intellij.psi.SqlElExpr -import org.jetbrains.kotlin.idea.base.util.module /** * Directive information for static property references @@ -31,7 +30,6 @@ class PsiStaticElement( private val originalFile: PsiFile, ) { private var fqdn = elExprList?.joinToString(".") { e -> e.text } ?: "" - private val module = originalFile.module constructor(elExprNames: String, file: PsiFile) : this(null, file) { fqdn = @@ -40,10 +38,12 @@ class PsiStaticElement( .substringBefore("@") } - fun getRefClazz(): PsiClass? = - module?.getJavaClazz(true, fqdn) + fun getRefClazz(): PsiClass? { + val project = originalFile.project + return project.getJavaClazz(fqdn) ?: JavaPsiFacade.getInstance(originalFile.project).findClass( fqdn, GlobalSearchScope.allScope(originalFile.project), ) + } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt index fbb2b1a1..14d2d917 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt @@ -16,178 +16,48 @@ package org.domaframework.doma.intellij.common.sql.directive import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.lookup.LookupElement -import com.intellij.codeInsight.lookup.LookupElementBuilder -import com.intellij.codeInsight.lookup.VariableLookupItem +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement -import com.intellij.psi.PsiManager -import com.intellij.psi.PsiType -import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.psi.PsiStaticElement -import org.domaframework.doma.intellij.extension.psi.psiClassType +import org.domaframework.doma.intellij.common.sql.directive.collector.StaticBuildFunctionCollector +import org.domaframework.doma.intellij.common.sql.directive.collector.StaticClassPackageCollector +import org.domaframework.doma.intellij.common.sql.directive.collector.StaticPropertyCollector import org.domaframework.doma.intellij.psi.SqlElClass import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes +import org.jetbrains.kotlin.idea.base.util.module class StaticDirectiveHandler( - private val originalFile: PsiElement, + originalFile: PsiElement, private val element: PsiElement, private val result: CompletionResultSet, private val bindText: String, private val project: Project, ) : DirectiveHandler(originalFile) { - /** - * Function information displayed with code completion for built-in functions - */ - data class DomaFunction( - val name: String, - val returnType: PsiType, - val parameters: List, - ) - - /** - * Show parameters in code completion for fields and methods - */ - data class CompletionSuggest( - val field: List, - val methods: List, - ) - override fun directiveHandle(): Boolean { var handleResult = false if (element.prevSibling is SqlElStaticFieldAccessExpr) { + handleResult = staticDirectiveHandler(element, result) + } + if (handleResult) return true + + if (PsiTreeUtil.nextLeaf(element)?.elementType == SqlTypes.AT_SIGN || + element.elementType == SqlTypes.AT_SIGN + ) { + val module = element.module ?: return false handleResult = - staticDirectiveHandler(element, result) { fqdn, bind -> - val psiStaticElement = PsiStaticElement(fqdn, originalFile.containingFile) - val javaClass = - psiStaticElement.getRefClazz() ?: return@staticDirectiveHandler null - val parentClazz = PsiParentClass(javaClass.psiClassType) - parentClazz.let { clazz -> - val fields = - clazz.searchStaticField(bind)?.map { f -> VariableLookupItem(f) } - val methods = - clazz.searchStaticMethod(bind)?.map { m -> - LookupElementBuilder - .create("${m.name}()") - .withPresentableText(m.name) - .withTailText(m.parameterList.text, true) - .withTypeText(m.returnType?.presentableText ?: "") - } - CompletionSuggest(fields ?: emptyList(), methods ?: emptyList()) - } - } - } else if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { + collectionModulePackages( + module, + result, + ) + } + if (handleResult) return true + + if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { // Built-in function completion - handleResult = - builtInDirectiveHandler(element, result) { bind -> - listOf( - DomaFunction( - "escape", - getJavaLangString(), - listOf( - getPsiTypeByClassName("java.lang.CharSequence"), - getPsiTypeByClassName("java.lang.Char"), - ), - ), - DomaFunction( - "prefix", - getJavaLangString(), - listOf( - getPsiTypeByClassName("java.lang.CharSequence"), - getPsiTypeByClassName("java.lang.Char"), - ), - ), - DomaFunction( - "infix", - getJavaLangString(), - listOf( - getPsiTypeByClassName("java.lang.CharSequence"), - getPsiTypeByClassName("java.lang.Char"), - ), - ), - DomaFunction( - "suffix", - getJavaLangString(), - listOf( - getPsiTypeByClassName("java.lang.CharSequence"), - getPsiTypeByClassName("java.lang.Char"), - ), - ), - DomaFunction( - "roundDownTimePart", - getPsiTypeByClassName("java.util.Date"), - listOf(getPsiTypeByClassName("java.util.Date")), - ), - DomaFunction( - "roundDownTimePart", - getPsiTypeByClassName("java.sql.Date"), - listOf(getPsiTypeByClassName("java.util.Date")), - ), - DomaFunction( - "roundDownTimePart", - getPsiTypeByClassName("java.sql.Timestamp"), - listOf(getPsiTypeByClassName("java.sql.Timestamp")), - ), - DomaFunction( - "roundDownTimePart", - getPsiTypeByClassName("java.time.LocalDateTime"), - listOf(getPsiTypeByClassName("java.time.LocalDateTime")), - ), - DomaFunction( - "roundUpTimePart", - getPsiTypeByClassName("java.util.Date"), - listOf(getPsiTypeByClassName("java.sql.Date")), - ), - DomaFunction( - "roundUpTimePart", - getPsiTypeByClassName("java.sql.Timestamp"), - listOf(getPsiTypeByClassName("java.sql.Timestamp")), - ), - DomaFunction( - "roundUpTimePart", - getPsiTypeByClassName("java.time.LocalDate"), - listOf(getPsiTypeByClassName("java.time.LocalDate")), - ), - DomaFunction( - "isEmpty", - getPsiTypeByClassName("boolean"), - listOf(getPsiTypeByClassName("java.lang.CharSequence")), - ), - DomaFunction( - "isNotEmpty", - getPsiTypeByClassName("boolean"), - listOf(getPsiTypeByClassName("java.lang.CharSequence")), - ), - DomaFunction( - "isBlank", - getPsiTypeByClassName("boolean"), - listOf(getPsiTypeByClassName("java.lang.CharSequence")), - ), - DomaFunction( - "isNotBlank", - getPsiTypeByClassName("boolean"), - listOf(getPsiTypeByClassName("java.lang.CharSequence")), - ), - ).filter { - it.name.startsWith(bind.substringAfter("@")) - }.map { - LookupElementBuilder - .create("${it.name}()") - .withPresentableText(it.name) - .withTailText( - "(${ - it.parameters.joinToString(",") { param -> - param.toString().replace("PsiType:", "") - } - })", - true, - ).withTypeText(it.returnType.presentableText) - } - } + handleResult = builtInDirectiveHandler(element, result) } return handleResult } @@ -195,39 +65,41 @@ class StaticDirectiveHandler( private fun staticDirectiveHandler( element: PsiElement, result: CompletionResultSet, - processor: (String, String) -> CompletionSuggest?, ): Boolean { val clazzRef = PsiTreeUtil .getChildOfType(element.prevSibling, SqlElClass::class.java) val fqdn = PsiTreeUtil.getChildrenOfTypeAsList(clazzRef, PsiElement::class.java).joinToString("") { it.text } - val candidates = processor(fqdn, bindText) ?: return false + + val collector = StaticPropertyCollector(element, bindText) + val candidates = collector.collectCompletionSuggest(fqdn) ?: return false result.addAllElements(candidates.field) candidates.methods.map { m -> result.addElement(m) } return true } + private fun collectionModulePackages( + module: Module, + result: CompletionResultSet, + ): Boolean { + val collector = StaticClassPackageCollector(element, module) + val candidates = collector.collect() ?: return false + result.addAllElements(candidates) + return true + } + private fun builtInDirectiveHandler( element: PsiElement, result: CompletionResultSet, - processor: (String) -> List?, ): Boolean { if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) { - val prefix = getBindSearchWord(element, "@") - val candidates = processor(prefix) + val prefix = getBindSearchWord(element, bindText) + val collector = StaticBuildFunctionCollector(project, prefix) + val candidates = collector.collect() candidates?.let { it1 -> result.addAllElements(it1) } return true } return false } - - private fun getJavaLangString(): PsiType = - PsiType.getJavaLangString( - PsiManager.getInstance(project), - GlobalSearchScope.allScope(project), - ) - - private fun getPsiTypeByClassName(className: String): PsiType = - PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project)) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandlerData.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandlerData.kt new file mode 100644 index 00000000..ee559197 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandlerData.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.common.sql.directive + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.VariableLookupItem +import com.intellij.icons.AllIcons +import com.intellij.psi.PsiType + +/** + * Function information displayed with code completion for built-in functions + */ +data class DomaFunction( + val name: String, + val returnType: PsiType, + val parameters: List, +) + +/** + * Show parameters in code completion for fields and methods + */ +data class CompletionSuggest( + val field: List, + val methods: List, +) + +data class StaticClassPackageSearchResult( + val packageName: String, + val qualifiedName: String, + val createText: String, + val fileType: String, +) + +val ICON_MAP = + mapOf( + "enum" to AllIcons.Nodes.Enum, + "annotation" to AllIcons.Nodes.Annotationtype, + "interface" to AllIcons.Nodes.Interface, + "record" to AllIcons.Nodes.Record, + "package" to AllIcons.Nodes.Package, + "JAVA" to AllIcons.FileTypes.Java, + "CLASS" to AllIcons.Nodes.Class, + ) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticBuildFunctionCollector.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticBuildFunctionCollector.kt new file mode 100644 index 00000000..618dd901 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticBuildFunctionCollector.kt @@ -0,0 +1,143 @@ +/* + * 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.common.sql.directive.collector + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiManager +import com.intellij.psi.PsiType +import com.intellij.psi.search.GlobalSearchScope +import org.domaframework.doma.intellij.common.sql.directive.DomaFunction + +class StaticBuildFunctionCollector( + private val project: Project, + private val bind: String, +) : StaticDirectiveHandlerCollector() { + public override fun collect(): List? = + listOf( + DomaFunction( + "escape", + getJavaLangString(), + listOf( + getPsiTypeByClassName("java.lang.CharSequence"), + getPsiTypeByClassName("java.lang.Char"), + ), + ), + DomaFunction( + "prefix", + getJavaLangString(), + listOf( + getPsiTypeByClassName("java.lang.CharSequence"), + getPsiTypeByClassName("java.lang.Char"), + ), + ), + DomaFunction( + "infix", + getJavaLangString(), + listOf( + getPsiTypeByClassName("java.lang.CharSequence"), + getPsiTypeByClassName("java.lang.Char"), + ), + ), + DomaFunction( + "suffix", + getJavaLangString(), + listOf( + getPsiTypeByClassName("java.lang.CharSequence"), + getPsiTypeByClassName("java.lang.Char"), + ), + ), + DomaFunction( + "roundDownTimePart", + getPsiTypeByClassName("java.util.Date"), + listOf(getPsiTypeByClassName("java.util.Date")), + ), + DomaFunction( + "roundDownTimePart", + getPsiTypeByClassName("java.sql.Date"), + listOf(getPsiTypeByClassName("java.util.Date")), + ), + DomaFunction( + "roundDownTimePart", + getPsiTypeByClassName("java.sql.Timestamp"), + listOf(getPsiTypeByClassName("java.sql.Timestamp")), + ), + DomaFunction( + "roundDownTimePart", + getPsiTypeByClassName("java.time.LocalDateTime"), + listOf(getPsiTypeByClassName("java.time.LocalDateTime")), + ), + DomaFunction( + "roundUpTimePart", + getPsiTypeByClassName("java.util.Date"), + listOf(getPsiTypeByClassName("java.sql.Date")), + ), + DomaFunction( + "roundUpTimePart", + getPsiTypeByClassName("java.sql.Timestamp"), + listOf(getPsiTypeByClassName("java.sql.Timestamp")), + ), + DomaFunction( + "roundUpTimePart", + getPsiTypeByClassName("java.time.LocalDate"), + listOf(getPsiTypeByClassName("java.time.LocalDate")), + ), + DomaFunction( + "isEmpty", + getPsiTypeByClassName("boolean"), + listOf(getPsiTypeByClassName("java.lang.CharSequence")), + ), + DomaFunction( + "isNotEmpty", + getPsiTypeByClassName("boolean"), + listOf(getPsiTypeByClassName("java.lang.CharSequence")), + ), + DomaFunction( + "isBlank", + getPsiTypeByClassName("boolean"), + listOf(getPsiTypeByClassName("java.lang.CharSequence")), + ), + DomaFunction( + "isNotBlank", + getPsiTypeByClassName("boolean"), + listOf(getPsiTypeByClassName("java.lang.CharSequence")), + ), + ).filter { + it.name.startsWith(bind.substringAfter("@")) + }.map { + LookupElementBuilder + .create("${it.name}()") + .withPresentableText(it.name) + .withTailText( + "(${ + it.parameters.joinToString(",") { param -> + param.toString().replace("PsiType:", "") + } + })", + true, + ).withTypeText(it.returnType.presentableText) + } + + private fun getJavaLangString(): PsiType = + PsiType.getJavaLangString( + PsiManager.getInstance(project), + GlobalSearchScope.allScope(project), + ) + + private fun getPsiTypeByClassName(className: String): PsiType = + PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project)) +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticClassPackageCollector.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticClassPackageCollector.kt new file mode 100644 index 00000000..5fb76d26 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticClassPackageCollector.kt @@ -0,0 +1,141 @@ +/* + * 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.common.sql.directive.collector + +import com.intellij.codeInsight.lookup.AutoCompletionPolicy +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.icons.AllIcons +import com.intellij.openapi.module.Module +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiPackage +import com.intellij.psi.search.GlobalSearchScope +import org.domaframework.doma.intellij.common.psi.PsiPatternUtil +import org.domaframework.doma.intellij.common.sql.directive.ICON_MAP +import org.domaframework.doma.intellij.common.sql.directive.StaticClassPackageSearchResult + +class StaticClassPackageCollector( + private val element: PsiElement, + private val module: Module, +) : StaticDirectiveHandlerCollector() { + public override fun collect(): List? { + val file = element.containingFile ?: return null + val packageNames = mutableSetOf() + + val prevPackageNames = + PsiPatternUtil.getBindSearchWord(file, element, "@").split(".") + val psiFacade = JavaPsiFacade.getInstance(module.project) ?: return null + val topPackages = psiFacade.findPackage("")?.subPackages + packageNames.addAll(searchPackage(module, topPackages, prevPackageNames)) + + return packageNames.mapNotNull { pkg -> + val icon = ICON_MAP[pkg.fileType] ?: AllIcons.FileTypes.Unknown + LookupElementBuilder + .create(pkg.createText) + .withPresentableText(pkg.qualifiedName) + .withTailText("(${pkg.packageName})", true) + .withIcon(icon) + .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) + } + } + + private fun searchPackage( + module: Module, + topPackages: Array?, + prevPackageNames: List = emptyList(), + ): MutableSet { + var subPackagesParent: PsiPackage? = null + var parentPackages: Array? = topPackages + + prevPackageNames.dropLast(1).forEach { pkg -> + subPackagesParent = + parentPackages + ?.find { it?.name?.contains(pkg) == true } + parentPackages = + subPackagesParent?.subPackages + } + + val packages = mutableSetOf() + + val targetSubPackages = + parentPackages + ?.mapNotNull { subPkg -> subPkg } + targetSubPackages?.let { pkg -> + packages.addAll( + pkg.map { + StaticClassPackageSearchResult( + it.qualifiedName, + it.name ?: "", + it.qualifiedName, + "package", + ) + }, + ) + } + + val files = + subPackagesParent + ?.getFiles(GlobalSearchScope.allScope(module.project)) + ?.mapNotNull { it } + ?: emptyList() + files.forEach { file -> + createPackageFileResult(file)?.let { packages.addAll(it) } + } + return packages + } + + private fun createPackageFileResult(file: PsiFile): List? { + val psiJavaFile = file as? PsiJavaFile ?: return emptyList() + val foundClasses = mutableListOf() + psiJavaFile.classes.forEach { topClazz -> + visitClass(topClazz, foundClasses) + } + + return foundClasses.map { clazz -> + val clazzName = clazz.name ?: "" + val packageName = clazz.qualifiedName?.replace(".$clazzName", "") ?: clazzName + StaticClassPackageSearchResult( + packageName, + clazzName, + "$packageName.$clazzName", + createFileType(clazz), + ) + } + } + + private fun visitClass( + clazz: PsiClass, + foundClasses: MutableList, + ) { + foundClasses += clazz + clazz.innerClasses.forEach { nested -> + visitClass(nested, foundClasses) + } + } + + private fun createFileType(type: PsiClass): String = + when { + type.isEnum -> "enum" + type.isAnnotationType -> "annotation" + type.isInterface -> "interface" + type.isRecord -> "record" + else -> type.containingFile?.fileType?.name ?: "" + }.toString() +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticDirectiveHandlerCollector.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticDirectiveHandlerCollector.kt new file mode 100644 index 00000000..ef74b6c2 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticDirectiveHandlerCollector.kt @@ -0,0 +1,25 @@ +/* + * 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.common.sql.directive.collector + +import com.intellij.codeInsight.lookup.LookupElement +import org.domaframework.doma.intellij.common.sql.directive.CompletionSuggest + +abstract class StaticDirectiveHandlerCollector { + protected open fun collect(): List? = null + + protected open fun collectCompletionSuggest(fqdn: String): CompletionSuggest? = null +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticPropertyCollector.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticPropertyCollector.kt new file mode 100644 index 00000000..09716fb4 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticPropertyCollector.kt @@ -0,0 +1,51 @@ +/* + * 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.common.sql.directive.collector + +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.codeInsight.lookup.VariableLookupItem +import com.intellij.icons.AllIcons +import com.intellij.psi.PsiElement +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.sql.directive.CompletionSuggest +import org.domaframework.doma.intellij.extension.psi.psiClassType + +class StaticPropertyCollector( + private val element: PsiElement, + private val bind: String, +) : StaticDirectiveHandlerCollector() { + public override fun collectCompletionSuggest(fqdn: String): CompletionSuggest? { + val psiStaticElement = PsiStaticElement(fqdn, element.containingFile) + val javaClass = + psiStaticElement.getRefClazz() ?: return null + val parentClazz = PsiParentClass(javaClass.psiClassType) + parentClazz.let { clazz -> + val fields = + clazz.searchStaticField(bind)?.map { f -> VariableLookupItem(f) } + val methods = + clazz.searchStaticMethod(bind)?.map { m -> + LookupElementBuilder + .create("${m.name}()") + .withPresentableText(m.name) + .withTailText(m.parameterList.text, true) + .withIcon(AllIcons.Nodes.Method) + .withTypeText(m.returnType?.presentableText ?: "") + } + return CompletionSuggest(fields ?: emptyList(), methods ?: emptyList()) + } + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationClassPathResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationClassPathResult.kt new file mode 100644 index 00000000..c64096b5 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationClassPathResult.kt @@ -0,0 +1,50 @@ +/* + * 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.common.sql.validator.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.domaframework.doma.intellij.bundle.MessageBundle +import org.domaframework.doma.intellij.common.psi.PsiParentClass + +/** + * Non-existent package name or class + */ +open class ValidationClassPathResult( + override val identify: PsiElement, + override val shortName: String, +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + project: Project, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message( + "inspection.invalid.sql.classpath", + identify.text ?: "", + ), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} 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 b02688fd..7db7e5c7 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 @@ -67,7 +67,6 @@ import org.domaframework.doma.intellij.psi.SqlElNeExpr import org.domaframework.doma.intellij.psi.SqlElOrExpr import org.domaframework.doma.intellij.psi.SqlElParameters import org.domaframework.doma.intellij.psi.SqlTypes -import org.jetbrains.kotlin.idea.base.util.module class SqlParameterCompletionProvider : CompletionProvider() { override fun addCompletions( @@ -392,7 +391,7 @@ class SqlParameterCompletionProvider : CompletionProvider( private fun getRefClazz( top: PsiElement, fqdnGetter: () -> String, - ): PsiClass? = top.module?.getJavaClazz(true, fqdnGetter()) + ): PsiClass? = top.project.getJavaClazz(fqdnGetter()) private fun setFieldsAndMethodsCompletionResultSet( fields: Array, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/extension/ProjectExtensions.kt b/src/main/kotlin/org/domaframework/doma/intellij/extension/ProjectExtensions.kt index eb474ec5..8528a76c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/extension/ProjectExtensions.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/extension/ProjectExtensions.kt @@ -19,8 +19,11 @@ import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager +import com.intellij.psi.search.GlobalSearchScope fun Project.getContentRoot(baseFile: VirtualFile): VirtualFile? = ProjectRootManager @@ -35,3 +38,11 @@ fun Project.getModule(virtualFile: VirtualFile): Module? = .getModuleForFile(virtualFile) fun Project.findFile(file: VirtualFile): PsiFile? = PsiManager.getInstance(this).findFile(file) + +fun Project.getJavaClazz(fqdn: String): PsiClass? { + val scope = GlobalSearchScope.allScope(this) + return JavaPsiFacade + .getInstance(this) + .findClasses(fqdn, scope) + .firstOrNull() +} 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 3f0177c8..65db7c0d 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 @@ -29,6 +29,7 @@ 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.cleanString +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationClassPathResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult import org.domaframework.doma.intellij.common.util.ForDirectiveUtil @@ -178,7 +179,15 @@ class SqlInspectionVisitor( ) { val blockElements = staticAccuser.accessElements val psiStaticClass = PsiStaticElement(staticAccuser.elClass.elIdExprList, staticAccuser.containingFile) - val referenceClass = psiStaticClass.getRefClazz() ?: return + val referenceClass = psiStaticClass.getRefClazz() + if (referenceClass == null) { + ValidationClassPathResult( + staticAccuser.elClass, + this.shortName, + ).highlightElement(holder) + return + } + val topParentClass = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccuser, referenceClass) if (topParentClass == null) { blockElements.firstOrNull()?.let { diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index 0d5f6acb..51c200ea 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -6,4 +6,5 @@ inspector.invalid.class.property=The field or method [{1}] does not exist in the inspection.dao.method.variable.error=There are unused parameters in the SQL [{0}] inspector.invalid.dao.parameter=The bind variable [{1}] does not exist in the Dao method [{0}] config.enable.sql.format=Enable SQL Format -inspection.invalid.sql.testdata=Bind variables must be followed by test data \ No newline at end of file +inspection.invalid.sql.testdata=Bind variables must be followed by test data +inspection.invalid.sql.classpath=A non-existent package or class name was specified. [{0}] \ No newline at end of file diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index 165a838b..269d3216 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -6,4 +6,5 @@ inspector.invalid.class.property=\u30AF\u30E9\u30B9[{0}]\u306B\u5B58\u5728\u3057 inspection.dao.method.variable.error=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u3044\u306A\u3044\u5F15\u6570\u304C\u3042\u308A\u307E\u3059[{0}] inspector.invalid.dao.parameter=Dao\u30E1\u30BD\u30C3\u30C9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] config.enable.sql.format=SQL\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u3092\u6709\u52B9\u5316 -inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 \ No newline at end of file +inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 +inspection.invalid.sql.classpath=\u0041\u0020\u006E\u006F\u006E\u002D\u0065\u0078\u0069\u0073\u0074\u0065\u006E\u0074\u0020\u0070\u0061\u0063\u006B\u0061\u0067\u0065\u0020\u006F\u0072\u0020\u0063\u006C\u0061\u0073\u0073\u0020\u006E\u0061\u006D\u0065\u0020\u0077\u0061\u0073\u0020\u0073\u0070\u0065\u0063\u0069\u0066\u0069\u0065\u0064\u002E [{0}] \ No newline at end of file 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 c5298294..902ef574 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 @@ -22,37 +22,41 @@ import org.domaframework.doma.intellij.inspection.sql.inspector.SqlBindVariableV * Code completion testing in SQL */ class SqlCompleteTest : DomaSqlTest() { + private val testDapName = "SqlCompleteTestDao" + override fun setUp() { super.setUp() addDaoJavaFile( - "SqlCompleteTestDao.java", + "$testDapName.java", ) addSqlFile( - "SqlCompleteTestDao/completeDaoArgument.sql", - "SqlCompleteTestDao/completeInstancePropertyFromDaoArgumentClass.sql", - "SqlCompleteTestDao/completeJavaPackageClass.sql", - "SqlCompleteTestDao/completeDirective.sql", - "SqlCompleteTestDao/completeStaticPropertyFromStaticPropertyCall.sql", - "SqlCompleteTestDao/completePropertyAfterStaticPropertyCall.sql", - "SqlCompleteTestDao/completeBuiltinFunction.sql", - "SqlCompleteTestDao/completeDirectiveInsideIf.sql", - "SqlCompleteTestDao/completeDirectiveInsideElseIf.sql", - "SqlCompleteTestDao/completeDirectiveInsideFor.sql", - "SqlCompleteTestDao/completeDirectiveFieldInsideIf.sql", - "SqlCompleteTestDao/completeDirectiveFieldInsideElseIf.sql", - "SqlCompleteTestDao/completeDirectiveFieldInsideFor.sql", - "SqlCompleteTestDao/completeConcatenationOperator.sql", - "SqlCompleteTestDao/completeComparisonOperator.sql", - "SqlCompleteTestDao/completeParameterFirst.sql", - "SqlCompleteTestDao/completeParameterFirstProperty.sql", - "SqlCompleteTestDao/completeParameterSecond.sql", - "SqlCompleteTestDao/completeParameterSecondProperty.sql", + "$testDapName/completeDaoArgument.sql", + "$testDapName/completeInstancePropertyFromDaoArgumentClass.sql", + "$testDapName/completeJavaPackageClass.sql", + "$testDapName/completeDirective.sql", + "$testDapName/completeStaticPropertyFromStaticPropertyCall.sql", + "$testDapName/completePropertyAfterStaticPropertyCall.sql", + "$testDapName/completeBuiltinFunction.sql", + "$testDapName/completeDirectiveInsideIf.sql", + "$testDapName/completeDirectiveInsideElseIf.sql", + "$testDapName/completeDirectiveInsideFor.sql", + "$testDapName/completeDirectiveFieldInsideIf.sql", + "$testDapName/completeDirectiveFieldInsideElseIf.sql", + "$testDapName/completeDirectiveFieldInsideFor.sql", + "$testDapName/completeConcatenationOperator.sql", + "$testDapName/completeComparisonOperator.sql", + "$testDapName/completeParameterFirst.sql", + "$testDapName/completeParameterFirstProperty.sql", + "$testDapName/completeParameterSecond.sql", + "$testDapName/completeParameterSecondProperty.sql", + "$testDapName/completeCallStaticPropertyClassPackage.sql", + "$testDapName/completeCallStaticPropertyClass.sql", ) myFixture.enableInspections(SqlBindVariableValidInspector()) } fun testCompleteDaoArgument() { - val sqlFile = findSqlFile("SqlCompleteTestDao/completeDaoArgument.sql") + val sqlFile = findSqlFile("$testDapName/completeDaoArgument.sql") assertNotNull("Not Found SQL File", sqlFile) if (sqlFile == null) return @@ -74,7 +78,7 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteInstancePropertyFromDaoArgumentClass() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeInstancePropertyFromDaoArgumentClass.sql", + "$testDapName/completeInstancePropertyFromDaoArgumentClass.sql", listOf( "employeeId", "employeeName", @@ -89,7 +93,7 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteJavaPackageClass() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeJavaPackageClass.sql", + "$testDapName/completeJavaPackageClass.sql", listOf( "CASE_INSENSITIVE_ORDER", "toString()", @@ -105,7 +109,7 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteDirective() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirective.sql", + "$testDapName/completeDirective.sql", listOf( "elseif", "else", @@ -124,7 +128,7 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteStaticPropertyFromStaticPropertyCall() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeStaticPropertyFromStaticPropertyCall.sql", + "$testDapName/completeStaticPropertyFromStaticPropertyCall.sql", listOf( "members", "projectNumber", @@ -139,9 +143,46 @@ class SqlCompleteTest : DomaSqlTest() { ) } + fun testCompleteCallStaticPropertyClassPackage() { + innerDirectiveCompleteTest( + "$testDapName/completeCallStaticPropertyClassPackage.sql", + listOf( + "doma", + "com", + "org", + ), + listOf( + "resources", + ), + ) + + innerDirectiveCompleteTest( + "$testDapName/completeCallStaticPropertyClass.sql", + listOf( + "doma.example.entity.Employee", + "doma.example.entity.EmployeeSummary", + "doma.example.entity.Principal.Permission", + "doma.example.entity.Principal", + "doma.example.entity.Project", + "doma.example.entity.ProjectDetail", + "doma.example.entity.Employee.Rank", + "doma.example.entity.User", + "doma.example.entity.UserSummary", + ), + listOf( + "resources", + "com", + "document", + "org", + "doma.example.dao.BatchDeleteTestDao", + "doma.example.dao.DeleteTestDao", + ), + ) + } + fun testCompletePropertyAfterStaticPropertyCall() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completePropertyAfterStaticPropertyCall.sql", + "$testDapName/completePropertyAfterStaticPropertyCall.sql", listOf( "managerId", ), @@ -154,7 +195,7 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteBuiltinFunction() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeBuiltinFunction.sql", + "$testDapName/completeBuiltinFunction.sql", listOf( "isEmpty()", "isNotEmpty()", @@ -174,17 +215,17 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteDirectiveInside() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveInsideIf.sql", + "$testDapName/completeDirectiveInsideIf.sql", listOf("employee"), listOf("project"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveInsideElseIf.sql", + "$testDapName/completeDirectiveInsideElseIf.sql", listOf("employee", "project"), emptyList(), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveInsideFor.sql", + "$testDapName/completeDirectiveInsideFor.sql", listOf("project"), listOf("employee"), ) @@ -192,17 +233,17 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteDirectiveFieldInside() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveFieldInsideIf.sql", + "$testDapName/completeDirectiveFieldInsideIf.sql", listOf("startsWith()"), listOf("employee", "project", "toLowCase"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveFieldInsideElseIf.sql", + "$testDapName/completeDirectiveFieldInsideElseIf.sql", listOf("department"), listOf("employee", "project"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeDirectiveFieldInsideFor.sql", + "$testDapName/completeDirectiveFieldInsideFor.sql", listOf( "projectId", "projectName", @@ -219,13 +260,13 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteConcatenationOperator() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeConcatenationOperator.sql", + "$testDapName/completeConcatenationOperator.sql", listOf("rank"), listOf("employee", "employeeId", "department"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeComparisonOperator.sql", + "$testDapName/completeComparisonOperator.sql", listOf("rank"), listOf("employee", "employeeId", "department"), ) @@ -233,25 +274,25 @@ class SqlCompleteTest : DomaSqlTest() { fun testCompleteParameter() { innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeParameterFirst.sql", + "$testDapName/completeParameterFirst.sql", listOf("employee"), listOf("employeeId", "department", "rank", "startWith"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeParameterFirstProperty.sql", + "$testDapName/completeParameterFirstProperty.sql", listOf("employeeId", "department", "rank"), listOf("employee"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeParameterSecond.sql", + "$testDapName/completeParameterSecond.sql", listOf("employee"), listOf("employeeId", "department", "rank", "startWith"), ) innerDirectiveCompleteTest( - "SqlCompleteTestDao/completeParameterSecondProperty.sql", + "$testDapName/completeParameterSecondProperty.sql", listOf("managerId"), listOf("employee", "department", "rank"), ) 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 3b712074..c5646c1b 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/document/SqlSymbolDocumentTestCase.kt @@ -114,8 +114,6 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { myFixture.configureFromExistingVirtualFile(sqlFile) var originalElement: PsiElement = myFixture.elementAtCaret - println("originalElement: ${originalElement.text}") - val resultDocument = myDocumentationProvider.generateDoc(originalElement, originalElement) assertEquals("Documentation should contain expected text", result, resultDocument) } @@ -131,8 +129,6 @@ class SqlSymbolDocumentTestCase : DomaSqlTest() { myFixture.configureFromExistingVirtualFile(sqlFile) var originalElement: PsiElement = myFixture.findElementByText(originalElementName, SqlElIdExpr::class.java) - println("originalElement: ${originalElement.text}") - val resultDocument = myDocumentationProvider.generateDoc(originalElement, originalElement) assertEquals("Documentation should contain expected text", result, resultDocument) } 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 99a6df0d..a05c03e3 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 @@ -36,6 +36,7 @@ class ParameterDefinedTest : DomaSqlTest() { "$testDaoName/batchAnnotationResolvesClassInList.sql", "$testDaoName/resolveDaoArgumentOfListType.sql", "$testDaoName/bindVariableInFunctionParameters.sql", + "$testDaoName/callStaticPropertyPackageName.sql", ) myFixture.enableInspections(SqlBindVariableValidInspector()) } @@ -68,6 +69,14 @@ class ParameterDefinedTest : DomaSqlTest() { myFixture.testHighlighting(false, false, false, sqlFile) } + fun testCallStaticPropertyPackageName() { + val sqlFile = findSqlFile("$testDaoName/callStaticPropertyPackageName.sql") + assertNotNull("Not Found SQL File", sqlFile) + if (sqlFile == null) return + + myFixture.testHighlighting(false, false, false, sqlFile) + } + fun testBatchAnnotationResolvesClassInList() { val sqlFile = findSqlFile("$testDaoName/batchAnnotationResolvesClassInList.sql") diff --git a/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt b/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt index d8ca88ec..2589c929 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt @@ -166,8 +166,6 @@ class SqlReferenceTestCase : DomaSqlTest() { for (reference in references) { val resolveResult = reference.references.firstOrNull()?.resolve() val expectedResults = resolveExpects[reference.text] - - println("Reference: ${reference.text}, Resolve Result: ${resolveResult?.toString()}, Expected Results: $expectedResults") assertTrue(expectedResults?.contains(resolveResult?.toString()) == true) } } 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 7c8c49a6..c8ea18fb 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 @@ -21,6 +21,9 @@ interface EmployeeSummaryDao { @Update(sqlFile=true) int accessStaticProperty(Project project,LocalDate referenceDate); + @Select + int callStaticPropertyPackageName(); + @Select ProjectDetail resolveDaoArgumentOfListType(List employees); 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 ea10c93e..4ec5bccf 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 @@ -69,4 +69,10 @@ interface SqlCompleteTestDao { @Select Employee completeParameterSecondProperty(Employee employee); + @Select + Employee completeCallStaticPropertyClassPackage(); + + @Select + Employee completeCallStaticPropertyClass(); + } \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/entity/Employee.java b/src/test/testData/src/main/java/doma/example/entity/Employee.java index a9af3501..e728c84a 100644 --- a/src/test/testData/src/main/java/doma/example/entity/Employee.java +++ b/src/test/testData/src/main/java/doma/example/entity/Employee.java @@ -31,4 +31,10 @@ private String getEmployeeRank() { public Integer employeeParam(Integer p1, Integer p2) { return p1 + p2; } + + enum Rank { + MANAGER, + STAFF, + INTERN + } } \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql new file mode 100644 index 00000000..2c90eb10 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql @@ -0,0 +1,4 @@ +Select COUNT(*) from employee + where rank = /* @doma.example.entity.EmployeeBase.Rank@MANAGER */1 + and exist_class = /* @doma.example.entity.Employee.Exists@T */false + and exist_pkg = /* @doma.example.entity@T */false \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClass.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClass.sql new file mode 100644 index 00000000..73ae81b5 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClass.sql @@ -0,0 +1,17 @@ +select + p.project_id + , p.statis + , p.project_name + , p.rank +from project p + inner join project_detail pd + on p.project_id = pd.project_id + where + p.project_id = /* detail.projectId */0 + /*%if @isNotEmpty(detail.members) */ + -- Code completion for static fields and methods + and pd.type = /* @doma.example.entity.@ */'TODO' + /*%for member: detail.members */ + and pd.member_id = /* member.memberId */0 + /*%end */ + /*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClassPackage.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClassPackage.sql new file mode 100644 index 00000000..f929ecb1 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClassPackage.sql @@ -0,0 +1,17 @@ +select + p.project_id + , p.statis + , p.project_name + , p.rank +from project p + inner join project_detail pd + on p.project_id = pd.project_id + where + p.project_id = /* detail.projectId */0 + /*%if @isNotEmpty(detail.members) */ + -- Code completion for static fields and methods + and pd.type = /* @@ */'TODO' + /*%for member: detail.members */ + and pd.member_id = /* member.memberId */0 + /*%end */ + /*%end */ \ No newline at end of file