Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 =
Expand All @@ -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),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,218 +16,90 @@
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<PsiType>,
)

/**
* Show parameters in code completion for fields and methods
*/
data class CompletionSuggest(
val field: List<VariableLookupItem>,
val methods: List<LookupElement>,
)

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
}

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<LookupElement>?,
): 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))
}
Original file line number Diff line number Diff line change
@@ -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<PsiType>,
)

/**
* Show parameters in code completion for fields and methods
*/
data class CompletionSuggest(
val field: List<VariableLookupItem>,
val methods: List<LookupElement>,
)

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,
)
Loading