From 4dd0725bee1d00a5c0b0a6aca0689f3bbc7bc556 Mon Sep 17 00:00:00 2001 From: xterao Date: Fri, 2 May 2025 10:05:53 +0900 Subject: [PATCH 1/6] Add package and Class name suggestions --- .../doma/intellij/common/FileTypeCheck.kt | 8 ++ .../sql/directive/StaticDirectiveHandler.kt | 86 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) 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/sql/directive/StaticDirectiveHandler.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandler.kt index fbb2b1a1..28647bec 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,22 +16,30 @@ package org.domaframework.doma.intellij.common.sql.directive import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.codeInsight.lookup.AutoCompletionPolicy import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.lookup.VariableLookupItem import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile 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.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiPatternUtil import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.sql.cleanString +import org.domaframework.doma.intellij.extension.getContentRoot import org.domaframework.doma.intellij.extension.psi.psiClassType 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.util.getSourceRoot class StaticDirectiveHandler( private val originalFile: PsiElement, @@ -80,7 +88,63 @@ class StaticDirectiveHandler( CompletionSuggest(fields ?: emptyList(), methods ?: emptyList()) } } - } else if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { + } + if (handleResult) return true + + if (PsiTreeUtil.nextLeaf(element)?.elementType == SqlTypes.AT_SIGN || + element.elementType == SqlTypes.AT_SIGN + ) { + handleResult = + staticClassPath( + result, + ) { file, root -> + val rootChildren = root.children + if (PsiTreeUtil.prevLeaf(element)?.elementType == SqlTypes.AT_SIGN) { + return@staticClassPath rootChildren.map { + LookupElementBuilder + .create(it.name) + .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) + } + } else { + val prevPackageNames = + PsiPatternUtil.getBindSearchWord(file, element, "@").split(".") + val topPackage = + rootChildren.firstOrNull { it.name == prevPackageNames.firstOrNull() } + ?: return@staticClassPath null + var nextPackage: VirtualFile? = + topPackage + if (prevPackageNames.size > 2) { + for (packageName in prevPackageNames.drop(1).dropLast(1)) { + if (nextPackage == null) break + nextPackage = + nextPackage.children.firstOrNull { + it.name == cleanString(packageName) + } + } + } + return@staticClassPath nextPackage + ?.children + ?.filter { + it.name.contains(cleanString(prevPackageNames.lastOrNull() ?: "")) + }?.map { + val packageName = prevPackageNames.joinToString(".").plus(it.nameWithoutExtension) + val suggestName = + it.nameWithoutExtension + if (!isJavaOrKotlinFileType(it)) { + suggestName.plus(".") + } + LookupElementBuilder + .create(packageName) + .withPresentableText(suggestName) + .withTailText("($packageName)", true) + .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) + } + } + } + } + if (handleResult) return true + + if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { // Built-in function completion handleResult = builtInDirectiveHandler(element, result) { bind -> @@ -208,13 +272,31 @@ class StaticDirectiveHandler( return true } + private fun staticClassPath( + result: CompletionResultSet, + processor: (PsiFile, VirtualFile) -> List?, + ): Boolean { + val file = originalFile.containingFile ?: return false + val virtualFile = file.virtualFile ?: return false + val root = + project + .getContentRoot(virtualFile) + ?.children + ?.firstOrNull() + ?.getSourceRoot(project) + ?: return false + val candidates = processor(file, root) ?: 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 prefix = getBindSearchWord(element, bindText) val candidates = processor(prefix) candidates?.let { it1 -> result.addAllElements(it1) } return true From 1bf5898b135ae6e40d40b7baa8125ecb53a15404 Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 7 May 2025 17:57:33 +0900 Subject: [PATCH 2/6] Implemented class package code completion for staticProperty calls --- .../intellij/common/psi/PsiStaticElement.kt | 8 +- .../sql/directive/StaticDirectiveHandler.kt | 252 ++---------------- .../directive/StaticDirectiveHandlerData.kt | 56 ++++ .../collector/StaticBuildFunctionCollector.kt | 143 ++++++++++ .../collector/StaticClassPackageCollector.kt | 141 ++++++++++ .../StaticDirectiveHandlerCollector.kt | 25 ++ .../collector/StaticPropertyCollector.kt | 51 ++++ .../SqlParameterCompletionProvider.kt | 3 +- .../intellij/extension/ProjectExtensions.kt | 11 + 9 files changed, 453 insertions(+), 237 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/StaticDirectiveHandlerData.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticBuildFunctionCollector.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticClassPackageCollector.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticDirectiveHandlerCollector.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/directive/collector/StaticPropertyCollector.kt 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 28647bec..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,242 +16,48 @@ package org.domaframework.doma.intellij.common.sql.directive import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.lookup.AutoCompletionPolicy -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.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -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.isJavaOrKotlinFileType -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.psi.PsiPatternUtil -import org.domaframework.doma.intellij.common.psi.PsiStaticElement -import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.extension.getContentRoot -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.util.getSourceRoot +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) { 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()) - } - } + 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 = - staticClassPath( + collectionModulePackages( + module, result, - ) { file, root -> - val rootChildren = root.children - if (PsiTreeUtil.prevLeaf(element)?.elementType == SqlTypes.AT_SIGN) { - return@staticClassPath rootChildren.map { - LookupElementBuilder - .create(it.name) - .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) - } - } else { - val prevPackageNames = - PsiPatternUtil.getBindSearchWord(file, element, "@").split(".") - val topPackage = - rootChildren.firstOrNull { it.name == prevPackageNames.firstOrNull() } - ?: return@staticClassPath null - var nextPackage: VirtualFile? = - topPackage - if (prevPackageNames.size > 2) { - for (packageName in prevPackageNames.drop(1).dropLast(1)) { - if (nextPackage == null) break - nextPackage = - nextPackage.children.firstOrNull { - it.name == cleanString(packageName) - } - } - } - return@staticClassPath nextPackage - ?.children - ?.filter { - it.name.contains(cleanString(prevPackageNames.lastOrNull() ?: "")) - }?.map { - val packageName = prevPackageNames.joinToString(".").plus(it.nameWithoutExtension) - val suggestName = - it.nameWithoutExtension - if (!isJavaOrKotlinFileType(it)) { - suggestName.plus(".") - } - LookupElementBuilder - .create(packageName) - .withPresentableText(suggestName) - .withTailText("($packageName)", true) - .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) - } - } - } + ) } 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 } @@ -259,33 +65,26 @@ 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 staticClassPath( + private fun collectionModulePackages( + module: Module, result: CompletionResultSet, - processor: (PsiFile, VirtualFile) -> List?, ): Boolean { - val file = originalFile.containingFile ?: return false - val virtualFile = file.virtualFile ?: return false - val root = - project - .getContentRoot(virtualFile) - ?.children - ?.firstOrNull() - ?.getSourceRoot(project) - ?: return false - val candidates = processor(file, root) ?: return false + val collector = StaticClassPackageCollector(element, module) + val candidates = collector.collect() ?: return false result.addAllElements(candidates) return true } @@ -293,23 +92,14 @@ class StaticDirectiveHandler( private fun builtInDirectiveHandler( element: PsiElement, result: CompletionResultSet, - processor: (String) -> List?, ): Boolean { if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) { val prefix = getBindSearchWord(element, bindText) - val candidates = processor(prefix) + 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/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() +} From 44b293d1df4802426f1e6fdb403e864b73dd0e6c Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 8 May 2025 10:34:08 +0900 Subject: [PATCH 3/6] Add ValidationClassPathResult for handling non-existent package or class --- .../result/ValidationClassPathResult.kt | 50 +++++++++++++++++++ .../sql/visitor/SqlInspectionVisitor.kt | 11 +++- .../messages/DomaToolsBundle.properties | 3 +- .../messages/DomaToolsBundle_ja.properties | 3 +- 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationClassPathResult.kt 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/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 From 04e1540efca864774ad1c87c3931be5e38aba054 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 8 May 2025 11:26:35 +0900 Subject: [PATCH 4/6] Add code completion for class names and package names for static property calls, and test cases for the Inspect feature. --- .../intellij/complate/sql/SqlCompleteTest.kt | 119 ++++++++++++------ .../inspection/sql/ParameterDefinedTest.kt | 9 ++ .../reference/SqlReferenceTestCase.kt | 4 +- .../doma/example/dao/EmployeeSummaryDao.java | 3 + .../doma/example/dao/SqlCompleteTestDao.java | 6 + .../java/doma/example/entity/Employee.java | 6 + .../callStaticPropertyPackageName.sql | 4 + .../completeCallStaticPropertyClass.sql | 17 +++ ...completeCallStaticPropertyClassPackage.sql | 17 +++ 9 files changed, 145 insertions(+), 40 deletions(-) create mode 100644 src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql create mode 100644 src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClass.sql create mode 100644 src/test/testData/src/main/resources/META-INF/doma/example/dao/SqlCompleteTestDao/completeCallStaticPropertyClassPackage.sql 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/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..a14a6bf7 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt @@ -167,7 +167,9 @@ class SqlReferenceTestCase : DomaSqlTest() { val resolveResult = reference.references.firstOrNull()?.resolve() val expectedResults = resolveExpects[reference.text] - println("Reference: ${reference.text}, Resolve Result: ${resolveResult?.toString()}, Expected Results: $expectedResults") + println( + "Reference: ${reference.text}, Resolve StaticClassPackageSearchResult: ${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 From afdecd9844a1ed247f4e84842867d2d00c80d222 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 8 May 2025 11:48:35 +0900 Subject: [PATCH 5/6] Update README.md to include details on class and package name checks for static property calls --- README.md | 4 ++++ images/complete_package.png | Bin 0 -> 14527 bytes images/inspectionPackageName.png | Bin 0 -> 15675 bytes 3 files changed, 4 insertions(+) create mode 100644 images/complete_package.png create mode 100644 images/inspectionPackageName.png 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 0000000000000000000000000000000000000000..e2f27d631a22e535d2149216eeef25158b60ea8d GIT binary patch literal 14527 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 literal 0 HcmV?d00001 diff --git a/images/inspectionPackageName.png b/images/inspectionPackageName.png new file mode 100644 index 0000000000000000000000000000000000000000..2feb26156f5bf8c7afe2abc191c174be1bc38543 GIT binary patch literal 15675 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 literal 0 HcmV?d00001 From 9837f7e0beb7aa986dd0e328068021aee83e1236 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 8 May 2025 11:54:46 +0900 Subject: [PATCH 6/6] Remove debug print statements from SqlSymbolDocumentTestCase and SqlReferenceTestCase --- .../doma/intellij/document/SqlSymbolDocumentTestCase.kt | 4 ---- .../doma/intellij/reference/SqlReferenceTestCase.kt | 4 ---- 2 files changed, 8 deletions(-) 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/reference/SqlReferenceTestCase.kt b/src/test/kotlin/org/domaframework/doma/intellij/reference/SqlReferenceTestCase.kt index a14a6bf7..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,10 +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 StaticClassPackageSearchResult: ${resolveResult?.toString()}, Expected Results: $expectedResults", - ) assertTrue(expectedResults?.contains(resolveResult?.toString()) == true) } }