Skip to content

Commit 6972dd9

Browse files
committed
A reference to the item defined in the for directive
1 parent 90e5f50 commit 6972dd9

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

src/main/kotlin/org/domaframework/doma/intellij/reference/SqlReference.kt

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,37 @@ import com.intellij.psi.PsiReferenceBase
2222
import com.intellij.psi.PsiType
2323
import com.intellij.psi.util.PsiTreeUtil
2424
import com.intellij.psi.util.PsiTypesUtil
25+
import com.intellij.psi.util.elementType
26+
import com.intellij.psi.util.nextLeafs
2527
import org.domaframework.doma.intellij.common.PluginLoggerUtil
2628
import org.domaframework.doma.intellij.common.dao.findDaoMethod
2729
import org.domaframework.doma.intellij.common.isSupportFileType
2830
import org.domaframework.doma.intellij.common.psi.PsiParentClass
2931
import org.domaframework.doma.intellij.common.psi.PsiStaticElement
3032
import org.domaframework.doma.intellij.extension.psi.findParameter
3133
import org.domaframework.doma.intellij.extension.psi.getDomaAnnotationType
34+
import org.domaframework.doma.intellij.extension.psi.getForItem
3235
import org.domaframework.doma.intellij.extension.psi.getIterableClazz
3336
import org.domaframework.doma.intellij.extension.psi.methodParameters
37+
import org.domaframework.doma.intellij.inspection.sql.inspector.SqlBindVariableValidInspector.BlockType
3438
import org.domaframework.doma.intellij.psi.SqlElClass
3539
import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr
40+
import org.domaframework.doma.intellij.psi.SqlElForDirective
3641
import org.domaframework.doma.intellij.psi.SqlElIdExpr
42+
import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr
3743
import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
44+
import org.domaframework.doma.intellij.psi.SqlTypes
3845

3946
class SqlReference(
4047
element: PsiElement,
4148
) : PsiReferenceBase<PsiElement>(element) {
4249
val file: PsiFile? = element.containingFile
4350

51+
data class ForIfDirectiveBlock(
52+
val type: BlockType,
53+
val element: PsiElement,
54+
)
55+
4456
override fun resolve(): PsiElement? {
4557
if (file == null || !isSupportFileType(file)) return null
4658
val startTime = System.nanoTime()
@@ -54,6 +66,18 @@ class SqlReference(
5466
val targetElement = getBlockCommentElements(element)
5567
if (targetElement.isEmpty()) return null
5668

69+
val topElm = targetElement.firstOrNull() as SqlElPrimaryExpr
70+
findInForDirectiveBlock(topElm)
71+
?.let {
72+
PluginLoggerUtil.countLogging(
73+
this::class.java.simpleName,
74+
"ReferenceForDirective",
75+
"Reference",
76+
startTime,
77+
)
78+
return it
79+
}
80+
5781
val daoMethod = findDaoMethod(file) ?: return null
5882

5983
return when (element.textOffset) {
@@ -94,7 +118,8 @@ class SqlReference(
94118
}
95119

96120
// Jump from field or method to definition (assuming the top element is static)
97-
val staticAccessParent = PsiTreeUtil.getParentOfType(staticDirection, SqlElStaticFieldAccessExpr::class.java)
121+
val staticAccessParent =
122+
PsiTreeUtil.getParentOfType(staticDirection, SqlElStaticFieldAccessExpr::class.java)
98123
if (staticAccessParent != null) {
99124
val firstChildText =
100125
staticAccessParent.children
@@ -234,4 +259,58 @@ class SqlReference(
234259
}
235260
return null
236261
}
262+
263+
private fun findInForDirectiveBlock(targetElement: PsiElement): PsiElement? {
264+
val forBlocks = getForDirectiveBlock(targetElement)
265+
val targetName =
266+
targetElement.text
267+
.replace("_has_next", "")
268+
.replace("_index", "")
269+
val matchForDirectiveItem = forBlocks.lastOrNull { it.element.text == targetName }
270+
if (matchForDirectiveItem != null) {
271+
return matchForDirectiveItem.element
272+
}
273+
return null
274+
}
275+
276+
private fun getForDirectiveBlock(targetElement: PsiElement): List<ForIfDirectiveBlock> {
277+
val topElm = targetElement.containingFile.firstChild ?: return emptyList()
278+
val directiveBlocks =
279+
topElm.nextLeafs
280+
.filter { elm ->
281+
elm.elementType == SqlTypes.EL_FOR ||
282+
elm.elementType == SqlTypes.EL_IF ||
283+
elm.elementType == SqlTypes.EL_END
284+
}.map {
285+
when (it.elementType) {
286+
SqlTypes.EL_FOR -> {
287+
(it.parent as? SqlElForDirective)
288+
?.getForItem()
289+
?.let { item ->
290+
ForIfDirectiveBlock(
291+
BlockType.FOR,
292+
item,
293+
)
294+
} ?: ForIfDirectiveBlock(
295+
BlockType.FOR,
296+
it,
297+
)
298+
}
299+
300+
SqlTypes.EL_IF -> ForIfDirectiveBlock(BlockType.IF, it)
301+
else -> ForIfDirectiveBlock(BlockType.END, it)
302+
}
303+
}
304+
val preBlocks =
305+
directiveBlocks
306+
.filter { it.element.textOffset <= targetElement.textOffset }
307+
val stack = mutableListOf<ForIfDirectiveBlock>()
308+
preBlocks.forEach { block ->
309+
when (block.type) {
310+
BlockType.FOR, BlockType.IF -> stack.add(block)
311+
BlockType.END -> if (stack.isNotEmpty()) stack.removeAt(stack.lastIndex)
312+
}
313+
}
314+
return stack.filter { it.type == BlockType.FOR }
315+
}
237316
}

0 commit comments

Comments
 (0)