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.idea.caches.resolve.analyze
|
||||
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.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -154,6 +155,7 @@ class KotlinLanguageInjector(
|
||||
?: injectWithCall(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
?: injectWithAnnotationEntry(place)
|
||||
}
|
||||
|
||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||
@@ -303,6 +305,25 @@ class KotlinLanguageInjector(
|
||||
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? {
|
||||
for (injection in injections) {
|
||||
if (injection.acceptsPsiElement(element)) {
|
||||
@@ -343,4 +364,4 @@ class KotlinLanguageInjector(
|
||||
|
||||
return InjectionInfo(id, prefix, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,4 +410,82 @@ class KotlinInjectionTest : AbstractInjectionTest() {
|
||||
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