diff --git a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt index 18ee5f0cf4e..ed6a682eac9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt +++ b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt @@ -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): InjectionInfo? { for (injection in injections) { if (injection.acceptsPsiElement(element)) { @@ -343,4 +364,4 @@ class KotlinLanguageInjector( return InjectionInfo(id, prefix, suffix) } -} \ No newline at end of file +} diff --git a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt index 882a74c25bc..bd96563bf60 100644 --- a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt @@ -410,4 +410,82 @@ class KotlinInjectionTest : AbstractInjectionTest() { ShredInfo(range(7, 15), hostRange=range(12, 22), prefix="s") ) ) -} \ No newline at end of file + + 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][a-z]+") + val name = "John" + """, + languageId = RegExpLanguage.INSTANCE.id, + unInjectShouldBePresent = false + ) + + doInjectionPresentTest( + javaText = """ + @interface Matches { String value(); } + """, + text = """ + @Matches(value = "[A-Z][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][a-z]+") + val name = "John" + """, + languageId = RegExpLanguage.INSTANCE.id, + unInjectShouldBePresent = false + ) + + doInjectionPresentTest( + text = """ + annotation class Matches(val pattern: String) + + @Matches(pattern = "[A-Z][a-z]+") + val name = "John" + """, + languageId = RegExpLanguage.INSTANCE.id, + unInjectShouldBePresent = false + ) + } + finally { + Configuration.getInstance().replaceInjections(listOf(), listOf(customInjection), true) + } + } +}