-
Notifications
You must be signed in to change notification settings - Fork 497
/
SqlDelightReferenceContributor.kt
90 lines (82 loc) · 3.66 KB
/
SqlDelightReferenceContributor.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package app.cash.sqldelight.intellij
import app.cash.sqldelight.core.lang.SqlDelightFile
import app.cash.sqldelight.core.lang.psi.ImportStmtMixin
import app.cash.sqldelight.core.lang.psi.JavaTypeMixin
import app.cash.sqldelight.core.lang.util.findChildrenOfType
import app.cash.sqldelight.core.psi.SqlDelightImportStmt
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.patterns.PlatformPatterns.psiElement
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceBase
import com.intellij.psi.PsiReferenceContributor
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.PsiReferenceRegistrar
import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelTypeAliasFqNameIndex
internal class SqlDelightReferenceContributor : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(
psiElement(JavaTypeMixin::class.java),
object : PsiReferenceProvider() {
override fun getReferencesByElement(
element: PsiElement,
context: ProcessingContext,
): Array<PsiReference> {
return arrayOf(JavaTypeReference(element as JavaTypeMixin))
}
},
)
}
internal class JavaTypeReference(element: JavaTypeMixin) :
PsiReferenceBase<JavaTypeMixin>(element, element.lastChild.textRangeInParent) {
override fun handleElementRename(newElementName: String): PsiElement {
return element.setName(newElementName)
}
override fun resolve(): PsiElement? {
val module = ModuleUtilCore.findModuleForPsiElement(element)
?: return null
val elementText = element.text
val qName = if (element.parent is SqlDelightImportStmt) {
elementText
} else {
val prefix = elementText.substringBefore('.')
val file = element.containingFile as SqlDelightFile
val withImport = file.sqlStmtList
?.findChildrenOfType<ImportStmtMixin>()
?.firstOrNull { it.javaType.text.endsWith(prefix) }
?.javaType?.text?.plus(elementText.removePrefix(prefix))
when {
withImport != null -> withImport
elementText.contains('.') -> elementText
else -> typeForThisPackage(file)
}
}
val project = element.project
val scope = module.getModuleWithDependenciesAndLibrariesScope(false)
val classNameIndex = KotlinFullClassNameIndex.getInstance()
val ktClass = { classNameIndex[qName, project, scope].firstOrNull() }
val typeAliasFqNameIndex = getKotlinTopLevelTypeAliasFqNameIndex()
val typeAlias = { typeAliasFqNameIndex[qName, project, scope].firstOrNull() }
val javaPsiFacade = JavaPsiFacade.getInstance(project)
val javaClass = { javaPsiFacade.findClass(qName, scope) }
return ktClass() ?: typeAlias() ?: javaClass()
}
private fun typeForThisPackage(file: SqlDelightFile) = "${file.packageName}.${element.text}"
}
}
private fun getKotlinTopLevelTypeAliasFqNameIndex(): KotlinTopLevelTypeAliasFqNameIndex {
// read the INSTANCE variable reflectively first (newer Kotlin plugins)
try {
val instanceField = KotlinTopLevelTypeAliasFqNameIndex::class.java.getField("INSTANCE")
val instance = instanceField.get(null)
if (instance is KotlinTopLevelTypeAliasFqNameIndex) {
return instance
}
} catch (e: Exception) {
/* intentionally empty, fall back to getInstance() call in case of errors */
}
return KotlinTopLevelTypeAliasFqNameIndex.getInstance()
}