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.InjectorUtils
|
||||
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.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
|
||||
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 partOffsetInParent = child.startOffsetInParent
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
val part = when (child) {
|
||||
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> {
|
||||
val partSize = children.subList(i, len).asSequence()
|
||||
.takeWhile { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
|
||||
.count()
|
||||
.takeWhile { it is KtLiteralStringTemplateEntry || it is KtEscapeStringTemplateEntry }
|
||||
.count()
|
||||
i += partSize - 1
|
||||
children[i]
|
||||
}
|
||||
is KtSimpleNameStringTemplateEntry -> {
|
||||
unparsable = true
|
||||
child.expression?.text ?: NO_VALUE_NAME
|
||||
}
|
||||
is KtBlockStringTemplateEntry -> {
|
||||
unparsable = true
|
||||
NO_VALUE_NAME
|
||||
}
|
||||
is KtSimpleNameStringTemplateEntry ->
|
||||
tryEvaluateConstant(child.expression) ?: run {
|
||||
unparsable = true
|
||||
child.expression?.text ?: NO_VALUE_NAME
|
||||
}
|
||||
is KtBlockStringTemplateEntry ->
|
||||
tryEvaluateConstant(child.expression) ?: run {
|
||||
unparsable = true
|
||||
NO_VALUE_NAME
|
||||
}
|
||||
else -> {
|
||||
unparsable = true
|
||||
child
|
||||
@@ -97,4 +104,12 @@ fun splitLiteralToInjectionParts(injection: BaseInjection, literal: KtStringTemp
|
||||
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"
|
||||
|
||||
@@ -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