Evaluating string constants in injections in String Interpolations (KT-25906)
This commit is contained in:
+25
-10
@@ -23,7 +23,11 @@ import com.intellij.psi.PsiLanguageInjectionHost
|
|||||||
import org.intellij.plugins.intelliLang.inject.InjectedLanguage
|
import org.intellij.plugins.intelliLang.inject.InjectedLanguage
|
||||||
import org.intellij.plugins.intelliLang.inject.InjectorUtils
|
import org.intellij.plugins.intelliLang.inject.InjectorUtils
|
||||||
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
|
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
data class InjectionSplitResult(val isUnparsable: Boolean, val ranges: List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>)
|
data class InjectionSplitResult(val isUnparsable: Boolean, val ranges: List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>)
|
||||||
@@ -54,22 +58,25 @@ fun splitLiteralToInjectionParts(injection: BaseInjection, literal: KtStringTemp
|
|||||||
val child = children[i]
|
val child = children[i]
|
||||||
val partOffsetInParent = child.startOffsetInParent
|
val partOffsetInParent = child.startOffsetInParent
|
||||||
|
|
||||||
|
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||||
val part = when (child) {
|
val part = when (child) {
|
||||||
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> {
|
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> {
|
||||||
val partSize = children.subList(i, len).asSequence()
|
val partSize = children.subList(i, len).asSequence()
|
||||||
.takeWhile { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
|
.takeWhile { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
|
||||||
.count()
|
.count()
|
||||||
i += partSize - 1
|
i += partSize - 1
|
||||||
children[i]
|
children[i]
|
||||||
}
|
}
|
||||||
is KtSimpleNameStringTemplateEntry -> {
|
is KtSimpleNameStringTemplateEntry ->
|
||||||
unparsable = true
|
tryEvaluateConstant(child.expression) ?: run {
|
||||||
child.expression?.text ?: NO_VALUE_NAME
|
unparsable = true
|
||||||
}
|
child.expression?.text ?: NO_VALUE_NAME
|
||||||
is KtBlockStringTemplateEntry -> {
|
}
|
||||||
unparsable = true
|
is KtBlockStringTemplateEntry ->
|
||||||
NO_VALUE_NAME
|
tryEvaluateConstant(child.expression) ?: run {
|
||||||
}
|
unparsable = true
|
||||||
|
NO_VALUE_NAME
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
unparsable = true
|
unparsable = true
|
||||||
child
|
child
|
||||||
@@ -97,4 +104,12 @@ fun splitLiteralToInjectionParts(injection: BaseInjection, literal: KtStringTemp
|
|||||||
return InjectionSplitResult(unparsable, result)
|
return InjectionSplitResult(unparsable, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tryEvaluateConstant(ktExpression: KtExpression?) =
|
||||||
|
ktExpression?.let { expression ->
|
||||||
|
ConstantExpressionEvaluator.getConstant(expression, expression.analyze())
|
||||||
|
?.takeUnless { it.isError }
|
||||||
|
?.getValue(TypeUtils.NO_EXPECTED_TYPE)
|
||||||
|
?.safeAs<String>()
|
||||||
|
}
|
||||||
|
|
||||||
private val NO_VALUE_NAME = "missingValue"
|
private val NO_VALUE_NAME = "missingValue"
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.psi.injection
|
||||||
|
|
||||||
|
import com.intellij.lang.html.HTMLLanguage
|
||||||
|
import com.intellij.lang.injection.InjectedLanguageManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.injection.Injectable
|
||||||
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.intellij.plugins.intelliLang.inject.InjectLanguageAction
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
|
||||||
|
class StringInterpolationInjectionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
|
||||||
|
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||||
|
|
||||||
|
fun testInterpolationSimpleName() = doTest(
|
||||||
|
"""
|
||||||
|
fun foo(){
|
||||||
|
val body = "Hello!"
|
||||||
|
"<html>${"$"}body</html><caret>"
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"<html>Hello!</html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun testInterpolationBlock() = doTest(
|
||||||
|
"""
|
||||||
|
const val a = "Hello "
|
||||||
|
|
||||||
|
fun foo(){
|
||||||
|
val body = "World!"
|
||||||
|
"<html>${"$"}{a + body}</html><caret>"
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
"<html>Hello World!</html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun doTest(text: String, expectedText: String) {
|
||||||
|
myFixture.configureByText("Injected.kt", text)
|
||||||
|
|
||||||
|
InjectLanguageAction.invokeImpl(
|
||||||
|
project,
|
||||||
|
myFixture.editor,
|
||||||
|
myFixture.file,
|
||||||
|
Injectable.fromLanguage(HTMLLanguage.INSTANCE)
|
||||||
|
)
|
||||||
|
|
||||||
|
val injectedElement = injectedElement ?: kotlin.test.fail("no injection")
|
||||||
|
TestCase.assertEquals(HTMLLanguage.INSTANCE, injectedElement.language)
|
||||||
|
val containingFile = injectedElement.containingFile
|
||||||
|
TestCase.assertEquals(expectedText, containingFile.text)
|
||||||
|
TestCase.assertFalse(
|
||||||
|
"Shouldn't be FRANKENSTEIN",
|
||||||
|
java.lang.Boolean.TRUE == containingFile.getUserData(InjectedLanguageManager.FRANKENSTEIN_INJECTION)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val injectedLanguageManager: InjectedLanguageManager
|
||||||
|
get() = InjectedLanguageManager.getInstance(project)
|
||||||
|
|
||||||
|
private val injectedElement: PsiElement?
|
||||||
|
get() = injectedLanguageManager.findInjectedElementAt(topLevelFile, topLevelCaretPosition)
|
||||||
|
|
||||||
|
private val topLevelFile: PsiFile get() = file.let { injectedLanguageManager.getTopLevelFile(it) }
|
||||||
|
|
||||||
|
private val topLevelCaretPosition get() = editor.caretModel.offset
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user