diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KotlinStringLiteralTextEscaper.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KotlinStringLiteralTextEscaper.kt index f3c182a5b2c..4a1001c89bd 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KotlinStringLiteralTextEscaper.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KotlinStringLiteralTextEscaper.kt @@ -41,6 +41,9 @@ class KotlinStringLiteralTextEscaper(host: KtStringTemplateExpression) : Literal is KtEscapeStringTemplateEntry -> { if (!rangeInsideHost.contains(childRange)) { //don't allow injection if its range starts or ends inside escaped sequence + //but still process offsets for the already decoded part + sourceOffsetsList.add(sourceOffset) + sourceOffsets = sourceOffsetsList.toNativeArray() return false } val unescaped = child.unescapedValue diff --git a/idea/tests/org/jetbrains/kotlin/psi/injection/StringInjectionHostTest.kt b/idea/tests/org/jetbrains/kotlin/psi/injection/StringInjectionHostTest.kt index a99f9a6e4e5..56a30b8d03b 100644 --- a/idea/tests/org/jetbrains/kotlin/psi/injection/StringInjectionHostTest.kt +++ b/idea/tests/org/jetbrains/kotlin/psi/injection/StringInjectionHostTest.kt @@ -104,6 +104,28 @@ class StringInjectionHostTest : KotlinTestWithEnvironment() { } } + fun testProvideOffsetsForDecodablePartOfUndecodableString() { + val undecodable = stringExpression(""""{\\d\}"""") + val escaper = undecodable.createLiteralTextEscaper() + val undecodableRange = undecodable.text.rangeOf("""\\d\""") + + val decoded = StringBuilder() + assertFalse(escaper.decode(undecodableRange, decoded)) + assertEquals("""\d""", decoded.toString()) + + val mapping = (0..undecodableRange.length).keysToMap { escaper.getOffsetInHost(it, undecodableRange) } + assertEquals( + mapOf( + 0 to 2, + 1 to 4, + 2 to 5, + 3 to -1, + 4 to -1 + ), + mapping + ) + } + private fun quoted(s: String): KtStringTemplateExpression { return stringExpression("\"$s\"") } @@ -163,3 +185,5 @@ class StringInjectionHostTest : KotlinTestWithEnvironment() { override fun createEnvironment() = createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY) } + +private fun String.rangeOf(inner: String): TextRange = indexOf(inner).let { TextRange.from(it, inner.length) }