diff --git a/idea/resources/injection/kotlinInjections.xml b/idea/resources/injection/kotlinInjections.xml
new file mode 100644
index 00000000000..60a1f265af6
--- /dev/null
+++ b/idea/resources/injection/kotlinInjections.xml
@@ -0,0 +1,9 @@
+
+
+
+ Kotlin
+
+
+
diff --git a/idea/src/META-INF/injection.xml b/idea/src/META-INF/injection.xml
index a313ac1337b..71ca3dc2b0f 100644
--- a/idea/src/META-INF/injection.xml
+++ b/idea/src/META-INF/injection.xml
@@ -5,5 +5,6 @@
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjectionSupport.kt b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjectionSupport.kt
index b06e9418409..3342a72cf3d 100644
--- a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjectionSupport.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjectionSupport.kt
@@ -24,7 +24,6 @@ import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTreeUtil.getDeepestLast
-import com.intellij.util.ArrayUtil
import com.intellij.util.Processor
import org.intellij.plugins.intelliLang.Configuration
import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport
@@ -32,6 +31,7 @@ import org.intellij.plugins.intelliLang.inject.InjectLanguageAction
import org.intellij.plugins.intelliLang.inject.InjectedLanguage
import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry
import org.jetbrains.annotations.NonNls
+import org.jetbrains.kotlin.idea.patterns.KotlinPatterns
import org.jetbrains.kotlin.idea.util.addAnnotation
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.findAnnotation
@@ -48,10 +48,12 @@ val LINE_LANGUAGE_REGEXP_COMMENT = Regex("//\\s*language=[\\w-]+")
class KotlinLanguageInjectionSupport : AbstractLanguageInjectionSupport() {
override fun getId(): String = KOTLIN_SUPPORT_ID
- override fun getPatternClasses() = ArrayUtil.EMPTY_CLASS_ARRAY
+ override fun getPatternClasses() = arrayOf(KotlinPatterns::class.java)
override fun isApplicableTo(host: PsiLanguageInjectionHost?) = host is KtElement
+ override fun useDefaultInjector(host: PsiLanguageInjectionHost?): Boolean = false
+
override fun addInjectionInPlace(language: Language?, host: PsiLanguageInjectionHost?): Boolean {
if (language == null || host == null) return false
diff --git a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt
index 84b056122b9..fc09b491195 100644
--- a/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt
@@ -26,10 +26,7 @@ import com.intellij.psi.util.PsiTreeUtil
import org.intellij.plugins.intelliLang.Configuration
import org.intellij.plugins.intelliLang.inject.InjectorUtils
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
-import org.jetbrains.kotlin.psi.KtCallExpression
-import org.jetbrains.kotlin.psi.KtElement
-import org.jetbrains.kotlin.psi.KtValueArgument
-import org.jetbrains.kotlin.psi.KtValueArgumentList
+import org.jetbrains.kotlin.psi.*
import java.util.*
class KotlinLanguageInjector : LanguageInjector {
@@ -66,19 +63,50 @@ class KotlinLanguageInjector : LanguageInjector {
for (reference in callee.references) {
ProgressManager.checkCanceled()
- val javaMethod = reference.resolve()
- if (javaMethod !is PsiMethod) continue
-
- val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
- val psiParameter = javaMethod.parameterList.parameters.getOrNull(argumentIndex) ?: continue
-
- val injections = Configuration.getInstance().getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)
-
- for (injection in injections) {
- if (injection.acceptsPsiElement(psiParameter)) {
- return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix)
+ val resolvedTo = reference.resolve()
+ if (resolvedTo is PsiMethod) {
+ val injectionForJavaMethod = injectionForJavaMethod(argument, resolvedTo)
+ if (injectionForJavaMethod != null) {
+ return injectionForJavaMethod
}
}
+ else if (resolvedTo is KtFunction) {
+ val injectionForJavaMethod = injectionForKotlinCall(argument, resolvedTo)
+ if (injectionForJavaMethod != null) {
+ return injectionForJavaMethod
+ }
+ }
+ }
+
+ return null
+ }
+
+ private fun injectionForJavaMethod(argument: KtValueArgument, javaMethod: PsiMethod): InjectionInfo? {
+ val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
+ val psiParameter = javaMethod.parameterList.parameters.getOrNull(argumentIndex) ?: return null
+
+ val injections = Configuration.getInstance().getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)
+
+ for (injection in injections) {
+ if (injection.acceptsPsiElement(psiParameter)) {
+ // ?? if (!processXmlInjections(injection, owner, method, paramIndex)) {
+ return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix)
+ }
+ }
+
+ return null
+ }
+
+ private fun injectionForKotlinCall(argument: KtValueArgument, ktFunction: KtFunction): InjectionInfo? {
+ val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
+ val ktParameter = ktFunction.valueParameters.getOrNull(argumentIndex) ?: return null
+
+ val injections = Configuration.getInstance().getInjections(KOTLIN_SUPPORT_ID)
+
+ for (injection in injections) {
+ if (injection.acceptsPsiElement(ktParameter)) {
+ return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix)
+ }
}
return null
diff --git a/idea/src/org/jetbrains/kotlin/idea/patterns/KotlinPatterns.kt b/idea/src/org/jetbrains/kotlin/idea/patterns/KotlinPatterns.kt
new file mode 100644
index 00000000000..7066ab97616
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/patterns/KotlinPatterns.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.patterns
+
+import com.intellij.patterns.*
+import com.intellij.util.PairProcessor
+import com.intellij.util.ProcessingContext
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtTypeReference
+import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
+import org.jetbrains.kotlin.renderer.DescriptorRenderer
+
+// Methods in this class are used through reflection
+@Suppress("unused")
+object KotlinPatterns: StandardPatterns() {
+ @JvmStatic fun psiParameter() = KtParameterPattern()
+ @JvmStatic fun psiMethod() = KotlinFunctionPattern()
+}
+
+// Methods in this class are used through reflection during pattern construction
+@Suppress("unused")
+open class KotlinFunctionPattern : PsiElementPattern(KtFunction::class.java) {
+ fun withParameters(vararg parameterTypes: String): KotlinFunctionPattern {
+ return withPatternCondition("kotlinFunctionPattern-withParameters") { function, context ->
+ if (function.valueParameters.size != parameterTypes.size) return@withPatternCondition false
+
+ val descriptor = function.resolveToDescriptorIfAny() as? FunctionDescriptor ?: return@withPatternCondition false
+ val valueParameters = descriptor.valueParameters
+
+ if (valueParameters.size != parameterTypes.size) return@withPatternCondition false
+ for (i in 0..valueParameters.size - 1) {
+ val expectedTypeString = parameterTypes[i]
+ val actualParameterDescriptor = valueParameters[i]
+
+ if (DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(actualParameterDescriptor.type) != expectedTypeString) {
+ return@withPatternCondition false
+ }
+ }
+
+ true
+ }
+ }
+
+ fun withReceiver(receiverFqName: String): KotlinFunctionPattern {
+ return withPatternCondition("kotlinFunctionPattern-withReceiver") { function, context ->
+ if (function.receiverTypeReference == null) return@withPatternCondition false
+ if (receiverFqName == "?") return@withPatternCondition true
+
+ val descriptor = function.resolveToDescriptorIfAny() as? FunctionDescriptor ?: return@withPatternCondition false
+ val receiver = descriptor.extensionReceiverParameter ?: return@withPatternCondition false
+
+ DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(receiver.type) == receiverFqName
+ }
+ }
+
+ fun definedInClass(fqName: String): KotlinFunctionPattern {
+ return withPatternCondition("kotlinFunctionPattern-definedInClass") { function, context ->
+ if (function.parent is KtFile) return@withPatternCondition false
+
+ function.containingClassOrObject?.fqName?.asString() == fqName
+ }
+ }
+
+ fun definedInPackage(packageFqName: String): KotlinFunctionPattern {
+ return withPatternCondition("kotlinFunctionPattern-definedInPackage") { function, context ->
+ if (function.parent !is KtFile) return@withPatternCondition false
+
+ function.getContainingKtFile().packageFqName.asString() == packageFqName
+ }
+ }
+
+ private fun withPatternCondition(debugName: String, condition: (KtFunction, ProcessingContext?) -> Boolean): KotlinFunctionPattern {
+ return with(object: PatternCondition(debugName) {
+ override fun accepts(function: KtFunction, context: ProcessingContext?): Boolean {
+ return condition(function, context)
+ }
+ })
+ }
+}
+
+// Methods in this class are used through reflection during pattern construction
+@Suppress("unused")
+class KtParameterPattern : PsiElementPattern(KtParameter::class.java) {
+ fun ofMethod(index: Int, pattern: ElementPattern): KtParameterPattern {
+ return with(object : PatternConditionPlus("KtParameterPattern-ofMethod", pattern) {
+ override fun processValues(ktParameter: KtParameter,
+ context: ProcessingContext,
+ processor: PairProcessor): Boolean {
+ return processor.process(ktParameter.ownerFunction, context)
+ }
+
+ override fun accepts(ktParameter: KtParameter, context: ProcessingContext): Boolean {
+ val ktFunction = ktParameter.ownerFunction ?: return false
+
+ val parameters = ktFunction.valueParameters
+ if (index < 0 || index >= parameters.size || ktParameter != parameters[index]) return false
+
+ return super.accepts(ktParameter, context)
+ }
+ })
+ }
+}
+
diff --git a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt
index a54d256f545..a288cc8e178 100644
--- a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt
+++ b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt
@@ -31,6 +31,7 @@ import org.intellij.plugins.intelliLang.inject.config.InjectionPlace
import org.intellij.plugins.intelliLang.references.FileReferenceInjector
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
+import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
class KotlinInjectionTest : KotlinLightCodeInsightFixtureTestCase() {
fun testInjectUnInjectOnSimpleString() {
@@ -57,6 +58,14 @@ class KotlinInjectionTest : KotlinLightCodeInsightFixtureTestCase() {
unInjectShouldBePresent = false
)
+ fun testInjectionOnKotlinPredefinedMethodWithRuntime() = testInjectionPresent(
+ """
+ |val test1 = kotlin.text.Regex("some")
+ """,
+ RegExpLanguage.INSTANCE.id,
+ unInjectShouldBePresent = false
+ )
+
fun testInjectionOnJavaCustomInjectionWithAnnotation() {
val customInjection = BaseInjection("java")
customInjection.injectedLanguageId = HTMLLanguage.INSTANCE.id
@@ -285,11 +294,12 @@ class KotlinInjectionTest : KotlinLightCodeInsightFixtureTestCase() {
)
override fun getProjectDescriptor(): LightProjectDescriptor {
- if (getTestName(true).endsWith("WithAnnotation")) {
- return KotlinLightProjectDescriptor.INSTANCE
+ val testName = getTestName(true)
+ return when {
+ testName.endsWith("WithAnnotation") -> KotlinLightProjectDescriptor.INSTANCE
+ testName.endsWith("WithRuntime") -> KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
+ else -> JAVA_LATEST
}
-
- return JAVA_LATEST
}
private fun testInjectionPresent(text: String, languageId: String? = null, unInjectShouldBePresent: Boolean = true) {