Recognize injections in annotations (KT-13636)
Recognize patterns from both Java (psiMethod) and Kotlin (kotlinParameter) that match the annotation argument.
This commit is contained in:
committed by
Nikolay Krasko
parent
b7ba45c001
commit
6f350c032d
@@ -38,6 +38,7 @@ import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
|
|||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.references.KtReference
|
import org.jetbrains.kotlin.idea.references.KtReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.runInReadActionWithWriteActionPriority
|
import org.jetbrains.kotlin.idea.runInReadActionWithWriteActionPriority
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -154,6 +155,7 @@ class KotlinLanguageInjector(
|
|||||||
?: injectWithCall(place)
|
?: injectWithCall(place)
|
||||||
?: injectWithReceiver(place)
|
?: injectWithReceiver(place)
|
||||||
?: injectWithVariableUsage(place, originalHost)
|
?: injectWithVariableUsage(place, originalHost)
|
||||||
|
?: injectWithAnnotationEntry(place)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||||
@@ -303,6 +305,25 @@ class KotlinLanguageInjector(
|
|||||||
return InjectionInfo(languageId, null, null)
|
return InjectionInfo(languageId, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun injectWithAnnotationEntry(host: KtElement): InjectionInfo? {
|
||||||
|
val argument = host.parent as? KtValueArgument ?: return null
|
||||||
|
val annotationEntry = PsiTreeUtil.getParentOfType(host, KtAnnotationEntry::class.java) ?: return null
|
||||||
|
if (isAnalyzeOff(host.project)) return null
|
||||||
|
|
||||||
|
val calleeReference = annotationEntry.calleeExpression?.constructorReferenceExpression?.mainReference
|
||||||
|
val callee = calleeReference?.resolve()
|
||||||
|
return when (callee) {
|
||||||
|
is KtFunction -> injectionForKotlinCall(argument, callee, calleeReference)
|
||||||
|
is PsiClass -> {
|
||||||
|
// Look for java injections for the PsiAnnotationMethod.
|
||||||
|
(argument.reference ?: argument.getArgumentName()?.referenceExpression?.mainReference)
|
||||||
|
?.let { it.resolve() as? PsiMethod }
|
||||||
|
?.let { findInjection(it, Configuration.getInstance().getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) }
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun findInjection(element: PsiElement?, injections: List<BaseInjection>): InjectionInfo? {
|
private fun findInjection(element: PsiElement?, injections: List<BaseInjection>): InjectionInfo? {
|
||||||
for (injection in injections) {
|
for (injection in injections) {
|
||||||
if (injection.acceptsPsiElement(element)) {
|
if (injection.acceptsPsiElement(element)) {
|
||||||
|
|||||||
@@ -410,4 +410,82 @@ class KotlinInjectionTest : AbstractInjectionTest() {
|
|||||||
ShredInfo(range(7, 15), hostRange=range(12, 22), prefix="s")
|
ShredInfo(range(7, 15), hostRange=range(12, 22), prefix="s")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun testJavaAnnotationsPattern() {
|
||||||
|
val customInjection = BaseInjection("java")
|
||||||
|
customInjection.injectedLanguageId = RegExpLanguage.INSTANCE.id
|
||||||
|
val elementPattern = customInjection.compiler.createElementPattern(
|
||||||
|
"""psiMethod().withName("value").withParameters().definedInClass("Matches")""",
|
||||||
|
"temp rule")
|
||||||
|
customInjection.setInjectionPlaces(InjectionPlace(elementPattern, true))
|
||||||
|
|
||||||
|
try {
|
||||||
|
Configuration.getInstance().replaceInjections(listOf(customInjection), listOf(), true)
|
||||||
|
|
||||||
|
doInjectionPresentTest(
|
||||||
|
javaText = """
|
||||||
|
@interface Matches { String value(); }
|
||||||
|
""",
|
||||||
|
text = """
|
||||||
|
@Matches("[A-Z]<caret>[a-z]+")
|
||||||
|
val name = "John"
|
||||||
|
""",
|
||||||
|
languageId = RegExpLanguage.INSTANCE.id,
|
||||||
|
unInjectShouldBePresent = false
|
||||||
|
)
|
||||||
|
|
||||||
|
doInjectionPresentTest(
|
||||||
|
javaText = """
|
||||||
|
@interface Matches { String value(); }
|
||||||
|
""",
|
||||||
|
text = """
|
||||||
|
@Matches(value = "[A-Z]<caret>[a-z]+")
|
||||||
|
val name = "John"
|
||||||
|
""",
|
||||||
|
languageId = RegExpLanguage.INSTANCE.id,
|
||||||
|
unInjectShouldBePresent = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Configuration.getInstance().replaceInjections(listOf(), listOf(customInjection), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testKotlinAnnotationsPattern() {
|
||||||
|
val customInjection = BaseInjection("kotlin")
|
||||||
|
customInjection.injectedLanguageId = RegExpLanguage.INSTANCE.id
|
||||||
|
val elementPattern = customInjection.compiler.createElementPattern(
|
||||||
|
"""kotlinParameter().ofFunction(0, kotlinFunction().withName("Matches").definedInClass("Matches"))""".trimIndent(),
|
||||||
|
"temp rule")
|
||||||
|
customInjection.setInjectionPlaces(InjectionPlace(elementPattern, true))
|
||||||
|
|
||||||
|
try {
|
||||||
|
Configuration.getInstance().replaceInjections(listOf(customInjection), listOf(), true)
|
||||||
|
|
||||||
|
doInjectionPresentTest(
|
||||||
|
text = """
|
||||||
|
annotation class Matches(val pattern: String)
|
||||||
|
|
||||||
|
@Matches("[A-Z]<caret>[a-z]+")
|
||||||
|
val name = "John"
|
||||||
|
""",
|
||||||
|
languageId = RegExpLanguage.INSTANCE.id,
|
||||||
|
unInjectShouldBePresent = false
|
||||||
|
)
|
||||||
|
|
||||||
|
doInjectionPresentTest(
|
||||||
|
text = """
|
||||||
|
annotation class Matches(val pattern: String)
|
||||||
|
|
||||||
|
@Matches(pattern = "[A-Z]<caret>[a-z]+")
|
||||||
|
val name = "John"
|
||||||
|
""",
|
||||||
|
languageId = RegExpLanguage.INSTANCE.id,
|
||||||
|
unInjectShouldBePresent = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Configuration.getInstance().replaceInjections(listOf(), listOf(customInjection), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user