Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
Expand Down Expand Up @@ -225,7 +226,8 @@ class ForDirectiveUtil {
var isBatchAnnotation = false
val forItemDeclarationBlocks =
if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) {
val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr
val staticFieldAccessExpr =
forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr
staticFieldAccessExpr.accessElements
} else {
forDirectiveDeclaration.getDeclarationChildren()
Expand All @@ -234,18 +236,25 @@ class ForDirectiveUtil {
// Defined by StaticFieldAccess
if (forDirectiveDeclaration.element is SqlElStaticFieldAccessExpr) {
val file = topForDirectiveItem.containingFile
val staticFieldAccessExpr = forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr
val staticFieldAccessExpr =
forDirectiveDeclaration.element as SqlElStaticFieldAccessExpr
val clazz = staticFieldAccessExpr.elClass
val staticElement = PsiStaticElement(clazz.elIdExprList, file)
val referenceClazz = staticElement.getRefClazz() ?: return null

// In the case of staticFieldAccess, the property that is called first is retrieved.
fieldAccessTopParentClass = getStaticFieldAccessTopElementClassType(staticFieldAccessExpr, referenceClazz)
fieldAccessTopParentClass =
getStaticFieldAccessTopElementClassType(
staticFieldAccessExpr,
referenceClazz,
)
} else {
// Defined by Dao parameter
val file = topForDirectiveItem.containingFile ?: return null
val daoMethod = findDaoMethod(file) ?: return null
val topElementText = forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text ?: return null
val topElementText =
forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text
?: return null
isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation()

val matchParam = daoMethod.findParameter(cleanString(topElementText))
Expand Down Expand Up @@ -450,5 +459,28 @@ class ForDirectiveUtil {
} else {
""
}

fun resolveForDirectiveClassTypeIfSuffixExists(
project: Project,
searchName: String,
): PsiType? {
if (searchName.endsWith("_has_next")) {
return PsiType.getTypeByName(
"java.lang.Boolean",
project,
GlobalSearchScope.allScope(project),
)
}

if (searchName.endsWith("_index")) {
return PsiType.getTypeByName(
"java.lang.Integer",
project,
GlobalSearchScope.allScope(project),
)
}

return null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -419,20 +419,28 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
positionText: String,
result: CompletionResultSet,
): Boolean {
val searchWord = cleanString(positionText)
val project = top.project
val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(top)
ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false

val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) ?: return false
val specifiedClassType = ForDirectiveUtil.resolveForDirectiveClassTypeIfSuffixExists(project, top.text)
val topClassType =
if (specifiedClassType != null) {
PsiParentClass(specifiedClassType)
} else {
forItemClassType
}

val result =
ForDirectiveUtil.getFieldAccessLastPropertyClassType(
elements,
project,
forItemClassType,
topClassType,
shortName = "",
dropLastIndex = 1,
complete = { lastType ->
val searchWord = cleanString(positionText)
setFieldsAndMethodsCompletionResultSet(
lastType.searchField(searchWord)?.toTypedArray() ?: emptyArray(),
lastType.searchMethod(searchWord)?.toTypedArray() ?: emptyArray(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,10 @@
package org.domaframework.doma.intellij.document

import com.intellij.lang.documentation.AbstractDocumentationProvider
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.domaframework.doma.intellij.common.dao.findDaoMethod
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
import org.domaframework.doma.intellij.common.psi.PsiParentClass
import org.domaframework.doma.intellij.common.psi.PsiStaticElement
import org.domaframework.doma.intellij.common.sql.foritem.ForItem
import org.domaframework.doma.intellij.common.util.ForDirectiveUtil
import org.domaframework.doma.intellij.extension.expr.accessElements
import org.domaframework.doma.intellij.extension.expr.accessElementsPrevOriginalElement
import org.domaframework.doma.intellij.extension.psi.findParameter
import org.domaframework.doma.intellij.extension.psi.getForItem
import org.domaframework.doma.intellij.extension.psi.psiClassType
import org.domaframework.doma.intellij.psi.SqlElClass
import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr
import org.domaframework.doma.intellij.document.generator.DocumentDaoParameterGenerator
import org.domaframework.doma.intellij.document.generator.DocumentStaticFieldGenerator
import org.domaframework.doma.intellij.psi.SqlElIdExpr
import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
import org.domaframework.doma.intellij.psi.SqlTypes
Expand All @@ -56,93 +43,26 @@ class ForItemElementDocumentationProvider : AbstractDocumentationProvider() {

val staticFieldAccessExpr =
PsiTreeUtil.getParentOfType(originalElement, SqlElStaticFieldAccessExpr::class.java)
if (staticFieldAccessExpr != null) {
generateStaticFieldDocument(
staticFieldAccessExpr,
file,
originalElement,
project,
result,
)
} else {
generateDaoFieldAccessDocument(originalElement, project, result)
}
return result.joinToString("\n")
}

private fun generateDaoFieldAccessDocument(
originalElement: PsiElement,
project: Project,
result: MutableList<String?>,
) {
var topParentType: PsiParentClass? = null
val selfSkip = isSelfSkip(originalElement)
val forDirectives = ForDirectiveUtil.getForDirectiveBlocks(originalElement, selfSkip)
val fieldAccessExpr =
PsiTreeUtil.getParentOfType(
originalElement,
SqlElFieldAccessExpr::class.java,
)
val fieldAccessBlocks =
fieldAccessExpr?.accessElementsPrevOriginalElement(originalElement.textOffset)
val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement

var isBatchAnnotation = false
if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) {
topParentType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives)
} else {
val daoMethod = findDaoMethod(originalElement.containingFile) ?: return
val param = daoMethod.findParameter(originalElement.text) ?: return
isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation()
topParentType = PsiParentClass(param.type)
}
if (fieldAccessExpr != null && fieldAccessBlocks != null) {
topParentType?.let {
ForDirectiveUtil.getFieldAccessLastPropertyClassType(
fieldAccessBlocks,
val generator =
if (staticFieldAccessExpr != null) {
DocumentStaticFieldGenerator(
originalElement,
project,
result,
staticFieldAccessExpr,
file,
)
} else {
DocumentDaoParameterGenerator(
originalElement,
project,
it,
isBatchAnnotation = isBatchAnnotation,
complete = { lastType ->
result.add("${generateTypeLink(lastType)} ${originalElement.text}")
},
result,
)
}
return
}
result.add("${generateTypeLink(topParentType)} ${originalElement.text}")
}

private fun generateStaticFieldDocument(
staticFieldAccessExpr: SqlElStaticFieldAccessExpr,
file: PsiFile,
originalElement: PsiElement,
project: Project,
result: MutableList<String?>,
) {
val fieldAccessBlocks = staticFieldAccessExpr.accessElements
val staticElement = PsiStaticElement(fieldAccessBlocks, file)
val referenceClass = staticElement.getRefClazz() ?: return
if (PsiTreeUtil.getParentOfType(originalElement, SqlElClass::class.java) != null) {
val clazzType = PsiParentClass(referenceClass.psiClassType)
result.add("${generateTypeLink(clazzType)} ${originalElement.text}")
return
}

ForDirectiveUtil.getFieldAccessLastPropertyClassType(
fieldAccessBlocks.filter { it.textOffset <= originalElement.textOffset },
project,
PsiParentClass(referenceClass.psiClassType),
complete = { lastType ->
result.add("${generateTypeLink(lastType)} ${originalElement.text}")
},
)
}
generator.generateDocument()

private fun isSelfSkip(targetElement: PsiElement): Boolean {
val forItem = ForItem(targetElement)
val forDirectiveExpr = forItem.getParentForDirectiveExpr()
return !(forDirectiveExpr != null && forDirectiveExpr.getForItem()?.textOffset == targetElement.textOffset)
return result.joinToString("\n")
}

override fun generateHoverDoc(
Expand All @@ -159,36 +79,4 @@ class ForItemElementDocumentationProvider : AbstractDocumentationProvider() {
result.add(typeDocument)
return result.joinToString("\n")
}

private fun generateTypeLink(parentClass: PsiParentClass?): String {
if (parentClass?.type != null) {
return generateTypeLinkFromCanonicalText(parentClass.type.canonicalText)
}
return ""
}

private fun generateTypeLinkFromCanonicalText(canonicalText: String): String {
val regex = Regex("([a-zA-Z0-9_]+\\.)*([a-zA-Z0-9_]+)")
val result = StringBuilder()
var lastIndex = 0

for (match in regex.findAll(canonicalText)) {
val fullMatch = match.value
val typeName = match.groups[2]?.value ?: fullMatch
val startIndex = match.range.first
val endIndex = match.range.last + 1

if (lastIndex < startIndex) {
result.append(canonicalText.substring(lastIndex, startIndex))
}
result.append("<a href=\"psi_element://$fullMatch\">$typeName</a>")
lastIndex = endIndex
}

if (lastIndex < canonicalText.length) {
result.append(canonicalText.substring(lastIndex))
}

return result.toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Doma Tools Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.domaframework.doma.intellij.document.generator

import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.domaframework.doma.intellij.common.dao.findDaoMethod
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
import org.domaframework.doma.intellij.common.psi.PsiParentClass
import org.domaframework.doma.intellij.common.util.ForDirectiveUtil
import org.domaframework.doma.intellij.extension.expr.accessElementsPrevOriginalElement
import org.domaframework.doma.intellij.extension.psi.findParameter
import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr

class DocumentDaoParameterGenerator(
val originalElement: PsiElement,
val project: Project,
val result: MutableList<String?>,
) : DocumentGenerator() {
override fun generateDocument() {
var topParentType: PsiParentClass? = null
val selfSkip = isSelfSkip(originalElement)
val forDirectives = ForDirectiveUtil.getForDirectiveBlocks(originalElement, selfSkip)
val fieldAccessExpr =
PsiTreeUtil.getParentOfType(
originalElement,
SqlElFieldAccessExpr::class.java,
)
val fieldAccessBlocks =
fieldAccessExpr?.accessElementsPrevOriginalElement(originalElement.textOffset)
val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement

var isBatchAnnotation = false
if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) {
val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives)
val specifiedClassType = ForDirectiveUtil.resolveForDirectiveClassTypeIfSuffixExists(project, searchElement.text)
topParentType =
if (specifiedClassType != null) {
PsiParentClass(specifiedClassType)
} else {
forItemClassType
}
} else {
val daoMethod = findDaoMethod(originalElement.containingFile) ?: return
val param = daoMethod.findParameter(originalElement.text) ?: return
isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation()
topParentType = PsiParentClass(param.type)
}

if (fieldAccessExpr != null && fieldAccessBlocks != null) {
topParentType?.let {
ForDirectiveUtil.getFieldAccessLastPropertyClassType(
fieldAccessBlocks,
project,
it,
isBatchAnnotation = isBatchAnnotation,
complete = { lastType ->
result.add("${generateTypeLink(lastType)} ${originalElement.text}")
},
)
}
return
}
result.add("${generateTypeLink(topParentType)} ${originalElement.text}")
}
}
Loading