Skip to content

Commit dd47f7f

Browse files
committed
Implement reference contributor for directives
1 parent f93e10c commit dd47f7f

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
<className>org.jetbrains.kotlin.test.helper.intentions.CreateContextualOverloadIntention</className>
4343
<category>Kotlin Compiler DevKit</category>
4444
</intentionAction>
45+
46+
<psi.referenceContributor implementation="org.jetbrains.kotlin.test.helper.reference.DirectiveReferenceContributor"/>
4547
</extensions>
4648

4749
<extensions defaultExtensionNs="org.jetbrains.kotlin">
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.jetbrains.kotlin.test.helper.reference
2+
3+
import com.intellij.openapi.util.TextRange
4+
import com.intellij.patterns.PlatformPatterns
5+
import com.intellij.psi.*
6+
import com.intellij.util.ProcessingContext
7+
import org.jetbrains.kotlin.test.helper.getTestDataType
8+
9+
class DirectiveReferenceContributor : PsiReferenceContributor() {
10+
val regex = Regex("// ([A-Z_]+)(:.*)?")
11+
val regexLanguageFeature = Regex("[+-]([A-Za-z0-9_]+)")
12+
13+
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
14+
registrar.registerReferenceProvider(
15+
PlatformPatterns.psiElement(PsiComment::class.java),
16+
object : PsiReferenceProvider() {
17+
override fun getReferencesByElement(
18+
element: PsiElement,
19+
context: ProcessingContext
20+
): Array<PsiReference> {
21+
val file =
22+
element.containingFile?.virtualFile ?: return PsiReference.EMPTY_ARRAY
23+
if (file.getTestDataType(element.project) == null) return PsiReference.EMPTY_ARRAY
24+
25+
val text = element.text
26+
val refs = mutableListOf<PsiReference>()
27+
28+
val regex = regex
29+
regex.matchAt(text, 0)?.let { match ->
30+
val (name, matchRange) = match.groups[1] ?: return@let
31+
val range = TextRange(matchRange.first, matchRange.last + 1)
32+
refs.add(TestDirectiveReference(element, range, name))
33+
34+
if (name == "LANGUAGE") {
35+
val (features, featuresRange) = match.groups[2] ?: return@let
36+
regexLanguageFeature.findAll(features).forEach {
37+
val (feature, featureRange) = it.groups[1] ?: return@forEach
38+
refs.add(
39+
LanguageFeatureReference(
40+
element,
41+
TextRange(
42+
featuresRange.first + featureRange.first,
43+
featuresRange.first + featureRange.last + 1,
44+
),
45+
feature
46+
)
47+
)
48+
}
49+
}
50+
}
51+
52+
return refs.toTypedArray()
53+
}
54+
}
55+
)
56+
}
57+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.jetbrains.kotlin.test.helper.reference
2+
3+
import com.intellij.openapi.util.TextRange
4+
import com.intellij.psi.JavaPsiFacade
5+
import com.intellij.psi.PsiElement
6+
import com.intellij.psi.PsiElementResolveResult
7+
import com.intellij.psi.PsiEnumConstant
8+
import com.intellij.psi.PsiPolyVariantReference
9+
import com.intellij.psi.PsiReferenceBase
10+
import com.intellij.psi.ResolveResult
11+
import com.intellij.psi.search.GlobalSearchScope
12+
13+
private const val LANGUAGE_FEATURE_FQ_NAME = "org.jetbrains.kotlin.config.LanguageFeature"
14+
15+
class LanguageFeatureReference(
16+
element: PsiElement,
17+
range: TextRange,
18+
private val key: String
19+
) : PsiReferenceBase<PsiElement>(element, range), PsiPolyVariantReference {
20+
21+
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
22+
val project = myElement.project
23+
val psiFacade = JavaPsiFacade.getInstance(project)
24+
val classes = psiFacade
25+
.findClasses(
26+
LANGUAGE_FEATURE_FQ_NAME,
27+
GlobalSearchScope.allScope(project)
28+
)
29+
30+
return classes.mapNotNull { clazz ->
31+
clazz.fields
32+
.filterIsInstance<PsiEnumConstant>()
33+
.firstOrNull { it.name == key }
34+
?.let { PsiElementResolveResult(it) }
35+
}.toTypedArray()
36+
}
37+
38+
override fun resolve(): PsiElement? {
39+
return multiResolve(false).singleOrNull()?.element
40+
}
41+
42+
override fun getVariants(): Array<Any> = emptyArray()
43+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.jetbrains.kotlin.test.helper.reference
2+
3+
import com.intellij.openapi.util.TextRange
4+
import com.intellij.psi.PsiElement
5+
import com.intellij.psi.PsiElementResolveResult
6+
import com.intellij.psi.PsiPolyVariantReference
7+
import com.intellij.psi.PsiReferenceBase
8+
import com.intellij.psi.ResolveResult
9+
import com.intellij.psi.search.GlobalSearchScope
10+
import org.jetbrains.kotlin.analysis.api.analyze
11+
import org.jetbrains.kotlin.idea.stubindex.KotlinPropertyShortNameIndex
12+
import org.jetbrains.kotlin.name.ClassId
13+
import org.jetbrains.kotlin.name.FqName
14+
15+
private val DIRECTIVE_CLASS_ID =
16+
ClassId.topLevel(FqName("org.jetbrains.kotlin.test.directives.model.Directive"))
17+
18+
class TestDirectiveReference(
19+
element: PsiElement,
20+
range: TextRange,
21+
private val key: String
22+
) : PsiReferenceBase<PsiElement>(element, range), PsiPolyVariantReference {
23+
24+
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
25+
val project = myElement.project
26+
27+
val declarations =
28+
KotlinPropertyShortNameIndex.Helper[key, project, GlobalSearchScope.allScope(project)]
29+
30+
return declarations
31+
.filter {
32+
analyze(it) {
33+
it.returnType.isSubtypeOf(DIRECTIVE_CLASS_ID)
34+
}
35+
}
36+
.map { PsiElementResolveResult(it) }.toTypedArray()
37+
}
38+
39+
override fun resolve(): PsiElement? {
40+
return multiResolve(false).singleOrNull()?.element
41+
}
42+
43+
override fun getVariants(): Array<Any> = emptyArray()
44+
}

0 commit comments

Comments
 (0)