KotlinLanguageInjector: injecting into returned literals (#KT-21673)
This commit is contained in:
@@ -45,12 +45,15 @@ import org.intellij.plugins.intelliLang.inject.config.InjectionPlace
|
||||
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
|
||||
import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.patterns.KotlinFunctionPattern
|
||||
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.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
@@ -172,10 +175,32 @@ class KotlinLanguageInjector(
|
||||
|
||||
private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? {
|
||||
return injectWithExplicitCodeInstruction(place)
|
||||
?: injectWithCall(place)
|
||||
?: injectInAnnotationCall(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
?: injectWithCall(place)
|
||||
?: injectReturnValue(place)
|
||||
?: injectInAnnotationCall(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
}
|
||||
|
||||
private fun injectReturnValue(place: KtElement): InjectionInfo? {
|
||||
val parent = place.parent
|
||||
|
||||
tailrec fun findReturnExpression(expression: PsiElement?): KtReturnExpression? = when (expression) {
|
||||
is KtReturnExpression -> expression
|
||||
is KtBinaryExpression -> findReturnExpression(expression.takeIf { it.operationToken == KtTokens.ELVIS }?.parent)
|
||||
is KtContainerNodeForControlStructureBody, is KtIfExpression -> findReturnExpression(expression.parent)
|
||||
else -> null
|
||||
}
|
||||
|
||||
val returnExp = findReturnExpression(parent) ?: return null
|
||||
|
||||
if (returnExp.labeledExpression != null) return null
|
||||
|
||||
val callableDeclaration = PsiTreeUtil.getParentOfType(returnExp, KtDeclaration::class.java) as? KtCallableDeclaration ?: return null
|
||||
if (callableDeclaration.annotationEntries.isEmpty()) return null
|
||||
|
||||
val descriptor = callableDeclaration.descriptor ?: return null
|
||||
return injectionInfoByAnnotation(descriptor)
|
||||
}
|
||||
|
||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||
@@ -339,7 +364,11 @@ class KotlinLanguageInjector(
|
||||
val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null
|
||||
|
||||
val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null
|
||||
val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
return injectionInfoByAnnotation(parameterDescriptor)
|
||||
}
|
||||
|
||||
private fun injectionInfoByAnnotation(annotated: Annotated): InjectionInfo? {
|
||||
val injectAnnotation = annotated.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
|
||||
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
|
||||
return InjectionInfo(languageId, null, null)
|
||||
|
||||
+31
-5
@@ -36,22 +36,22 @@ import org.intellij.plugins.intelliLang.inject.config.BaseInjection
|
||||
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
|
||||
import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
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.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import org.jetbrains.kotlin.util.aliasImportMap
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class KotlinLanguageInjector(
|
||||
val configuration: Configuration,
|
||||
@@ -158,11 +158,33 @@ class KotlinLanguageInjector(
|
||||
private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? {
|
||||
return injectWithExplicitCodeInstruction(place)
|
||||
?: injectWithCall(place)
|
||||
?: injectReturnValue(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
?: injectWithAnnotationEntry(place)
|
||||
}
|
||||
|
||||
private fun injectReturnValue(place: KtElement): InjectionInfo? {
|
||||
val parent = place.parent
|
||||
|
||||
tailrec fun findReturnExpression(expression: PsiElement?): KtReturnExpression? = when (expression) {
|
||||
is KtReturnExpression -> expression
|
||||
is KtBinaryExpression -> findReturnExpression(expression.takeIf { it.operationToken == KtTokens.ELVIS }?.parent)
|
||||
is KtContainerNodeForControlStructureBody, is KtIfExpression -> findReturnExpression(expression.parent)
|
||||
else -> null
|
||||
}
|
||||
|
||||
val returnExp = findReturnExpression(parent) ?: return null
|
||||
|
||||
if (returnExp.labeledExpression != null) return null
|
||||
|
||||
val callableDeclaration = PsiTreeUtil.getParentOfType(returnExp, KtDeclaration::class.java) as? KtCallableDeclaration ?: return null
|
||||
if (callableDeclaration.annotationEntries.isEmpty()) return null
|
||||
|
||||
val descriptor = callableDeclaration.descriptor ?: return null
|
||||
return injectionInfoByAnnotation(descriptor)
|
||||
}
|
||||
|
||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||
val support = kotlinSupport ?: return null
|
||||
return InjectionInfo.fromBaseInjection(support.findCommentInjection(host)) ?: support.findAnnotationInjectionLanguageId(host)
|
||||
@@ -300,7 +322,11 @@ class KotlinLanguageInjector(
|
||||
val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null
|
||||
|
||||
val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null
|
||||
val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
return injectionInfoByAnnotation(parameterDescriptor)
|
||||
}
|
||||
|
||||
private fun injectionInfoByAnnotation(annotated: Annotated): InjectionInfo? {
|
||||
val injectAnnotation = annotated.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
|
||||
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
|
||||
return InjectionInfo(languageId, null, null)
|
||||
|
||||
+30
-1
@@ -45,12 +45,15 @@ import org.intellij.plugins.intelliLang.inject.config.InjectionPlace
|
||||
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
|
||||
import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.patterns.KotlinFunctionPattern
|
||||
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.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
@@ -169,11 +172,33 @@ class KotlinLanguageInjector(
|
||||
private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? {
|
||||
return injectWithExplicitCodeInstruction(place)
|
||||
?: injectWithCall(place)
|
||||
?: injectReturnValue(place)
|
||||
?: injectInAnnotationCall(place)
|
||||
?: injectWithReceiver(place)
|
||||
?: injectWithVariableUsage(place, originalHost)
|
||||
}
|
||||
|
||||
private fun injectReturnValue(place: KtElement): InjectionInfo? {
|
||||
val parent = place.parent
|
||||
|
||||
tailrec fun findReturnExpression(expression: PsiElement?): KtReturnExpression? = when (expression) {
|
||||
is KtReturnExpression -> expression
|
||||
is KtBinaryExpression -> findReturnExpression(expression.takeIf { it.operationToken == KtTokens.ELVIS }?.parent)
|
||||
is KtContainerNodeForControlStructureBody, is KtIfExpression -> findReturnExpression(expression.parent)
|
||||
else -> null
|
||||
}
|
||||
|
||||
val returnExp = findReturnExpression(parent) ?: return null
|
||||
|
||||
if (returnExp.labeledExpression != null) return null
|
||||
|
||||
val callableDeclaration = PsiTreeUtil.getParentOfType(returnExp, KtDeclaration::class.java) as? KtCallableDeclaration ?: return null
|
||||
if (callableDeclaration.annotationEntries.isEmpty()) return null
|
||||
|
||||
val descriptor = callableDeclaration.descriptor ?: return null
|
||||
return injectionInfoByAnnotation(descriptor)
|
||||
}
|
||||
|
||||
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
|
||||
val support = kotlinSupport ?: return null
|
||||
return InjectionInfo.fromBaseInjection(support.findCommentInjection(host)) ?: support.findAnnotationInjectionLanguageId(host)
|
||||
@@ -335,7 +360,11 @@ class KotlinLanguageInjector(
|
||||
val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null
|
||||
|
||||
val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null
|
||||
val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
return injectionInfoByAnnotation(parameterDescriptor)
|
||||
}
|
||||
|
||||
private fun injectionInfoByAnnotation(annotated: Annotated): InjectionInfo? {
|
||||
val injectAnnotation = annotated.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
|
||||
|
||||
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
|
||||
return InjectionInfo(languageId, null, null)
|
||||
|
||||
@@ -612,4 +612,61 @@ class KotlinInjectionTest : AbstractInjectionTest() {
|
||||
}
|
||||
}
|
||||
|
||||
fun testInjectionOnReturnResultWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(): String {
|
||||
return "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionOnReturnResultWithElvisWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?): String {
|
||||
return arg ?: "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionOnReturnResultWithIfWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?): String {
|
||||
return if (arg == null) "<div>" else "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionExpressionBodyWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider() = "<ht<caret>ml></html>"
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionExpressionBodyElvisWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?) = arg ?: "<ht<caret>ml></html>"
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -506,4 +506,61 @@ class KotlinInjectionTest : AbstractInjectionTest() {
|
||||
Configuration.getInstance().replaceInjections(listOf(), listOf(customInjection), true)
|
||||
}
|
||||
}
|
||||
|
||||
fun testInjectionOnReturnResultWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(): String {
|
||||
return "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionOnReturnResultWithElvisWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?): String {
|
||||
return arg ?: "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionOnReturnResultWithIfWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?): String {
|
||||
return if (arg == null) "<div>" else "<ht<caret>ml></html>"
|
||||
}
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionExpressionBodyWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider() = "<ht<caret>ml></html>"
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
fun testInjectionExpressionBodyElvisWithAnnotation() = doInjectionPresentTest(
|
||||
"""
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
@Language("HTML")
|
||||
fun htmlProvider(arg: String?) = arg ?: "<ht<caret>ml></html>"
|
||||
""",
|
||||
languageId = HTMLLanguage.INSTANCE.id, unInjectShouldBePresent = false
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user