|
16 | 16 | package org.domaframework.doma.intellij.common.sql.directive |
17 | 17 |
|
18 | 18 | import com.intellij.codeInsight.completion.CompletionResultSet |
| 19 | +import com.intellij.codeInsight.lookup.AutoCompletionPolicy |
19 | 20 | import com.intellij.codeInsight.lookup.LookupElement |
20 | 21 | import com.intellij.codeInsight.lookup.LookupElementBuilder |
21 | 22 | import com.intellij.codeInsight.lookup.VariableLookupItem |
22 | 23 | import com.intellij.openapi.project.Project |
| 24 | +import com.intellij.openapi.vfs.VirtualFile |
23 | 25 | import com.intellij.psi.PsiElement |
| 26 | +import com.intellij.psi.PsiFile |
24 | 27 | import com.intellij.psi.PsiManager |
25 | 28 | import com.intellij.psi.PsiType |
26 | 29 | import com.intellij.psi.search.GlobalSearchScope |
27 | 30 | import com.intellij.psi.util.PsiTreeUtil |
28 | 31 | import com.intellij.psi.util.elementType |
| 32 | +import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType |
29 | 33 | import org.domaframework.doma.intellij.common.psi.PsiParentClass |
| 34 | +import org.domaframework.doma.intellij.common.psi.PsiPatternUtil |
30 | 35 | import org.domaframework.doma.intellij.common.psi.PsiStaticElement |
| 36 | +import org.domaframework.doma.intellij.common.sql.cleanString |
| 37 | +import org.domaframework.doma.intellij.extension.getContentRoot |
31 | 38 | import org.domaframework.doma.intellij.extension.psi.psiClassType |
32 | 39 | import org.domaframework.doma.intellij.psi.SqlElClass |
33 | 40 | import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr |
34 | 41 | import org.domaframework.doma.intellij.psi.SqlTypes |
| 42 | +import org.jetbrains.kotlin.idea.util.getSourceRoot |
35 | 43 |
|
36 | 44 | class StaticDirectiveHandler( |
37 | 45 | private val originalFile: PsiElement, |
@@ -80,7 +88,63 @@ class StaticDirectiveHandler( |
80 | 88 | CompletionSuggest(fields ?: emptyList(), methods ?: emptyList()) |
81 | 89 | } |
82 | 90 | } |
83 | | - } else if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { |
| 91 | + } |
| 92 | + if (handleResult) return true |
| 93 | + |
| 94 | + if (PsiTreeUtil.nextLeaf(element)?.elementType == SqlTypes.AT_SIGN || |
| 95 | + element.elementType == SqlTypes.AT_SIGN |
| 96 | + ) { |
| 97 | + handleResult = |
| 98 | + staticClassPath( |
| 99 | + result, |
| 100 | + ) { file, root -> |
| 101 | + val rootChildren = root.children |
| 102 | + if (PsiTreeUtil.prevLeaf(element)?.elementType == SqlTypes.AT_SIGN) { |
| 103 | + return@staticClassPath rootChildren.map { |
| 104 | + LookupElementBuilder |
| 105 | + .create(it.name) |
| 106 | + .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) |
| 107 | + } |
| 108 | + } else { |
| 109 | + val prevPackageNames = |
| 110 | + PsiPatternUtil.getBindSearchWord(file, element, "@").split(".") |
| 111 | + val topPackage = |
| 112 | + rootChildren.firstOrNull { it.name == prevPackageNames.firstOrNull() } |
| 113 | + ?: return@staticClassPath null |
| 114 | + var nextPackage: VirtualFile? = |
| 115 | + topPackage |
| 116 | + if (prevPackageNames.size > 2) { |
| 117 | + for (packageName in prevPackageNames.drop(1).dropLast(1)) { |
| 118 | + if (nextPackage == null) break |
| 119 | + nextPackage = |
| 120 | + nextPackage.children.firstOrNull { |
| 121 | + it.name == cleanString(packageName) |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + return@staticClassPath nextPackage |
| 126 | + ?.children |
| 127 | + ?.filter { |
| 128 | + it.name.contains(cleanString(prevPackageNames.lastOrNull() ?: "")) |
| 129 | + }?.map { |
| 130 | + val packageName = prevPackageNames.joinToString(".").plus(it.nameWithoutExtension) |
| 131 | + val suggestName = |
| 132 | + it.nameWithoutExtension |
| 133 | + if (!isJavaOrKotlinFileType(it)) { |
| 134 | + suggestName.plus(".") |
| 135 | + } |
| 136 | + LookupElementBuilder |
| 137 | + .create(packageName) |
| 138 | + .withPresentableText(suggestName) |
| 139 | + .withTailText("($packageName)", true) |
| 140 | + .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | + if (handleResult) return true |
| 146 | + |
| 147 | + if (element.prevSibling?.elementType == SqlTypes.AT_SIGN) { |
84 | 148 | // Built-in function completion |
85 | 149 | handleResult = |
86 | 150 | builtInDirectiveHandler(element, result) { bind -> |
@@ -208,13 +272,31 @@ class StaticDirectiveHandler( |
208 | 272 | return true |
209 | 273 | } |
210 | 274 |
|
| 275 | + private fun staticClassPath( |
| 276 | + result: CompletionResultSet, |
| 277 | + processor: (PsiFile, VirtualFile) -> List<LookupElement>?, |
| 278 | + ): Boolean { |
| 279 | + val file = originalFile.containingFile ?: return false |
| 280 | + val virtualFile = file.virtualFile ?: return false |
| 281 | + val root = |
| 282 | + project |
| 283 | + .getContentRoot(virtualFile) |
| 284 | + ?.children |
| 285 | + ?.firstOrNull() |
| 286 | + ?.getSourceRoot(project) |
| 287 | + ?: return false |
| 288 | + val candidates = processor(file, root) ?: return false |
| 289 | + result.addAllElements(candidates) |
| 290 | + return true |
| 291 | + } |
| 292 | + |
211 | 293 | private fun builtInDirectiveHandler( |
212 | 294 | element: PsiElement, |
213 | 295 | result: CompletionResultSet, |
214 | 296 | processor: (String) -> List<LookupElement>?, |
215 | 297 | ): Boolean { |
216 | 298 | if (BindDirectiveUtil.getDirectiveType(element) == DirectiveType.BUILT_IN) { |
217 | | - val prefix = getBindSearchWord(element, "@") |
| 299 | + val prefix = getBindSearchWord(element, bindText) |
218 | 300 | val candidates = processor(prefix) |
219 | 301 | candidates?.let { it1 -> result.addAllElements(it1) } |
220 | 302 | return true |
|
0 commit comments