diff --git a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
index 26caf932bde8..abac046aeaab 100644
--- a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
+++ b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties
@@ -2288,6 +2288,9 @@ inspection.can.be.primary.constructor.property.display.name=Property is explicit
inspection.can.unescape.dollar.literal.inspection.display.name=Escaped dollar characters in string can be simplified
inspection.can.unescape.dollar.literal.inspection.problem.description=Escaped dollar characters in the string can be simplified
replace.with.dollar.literals=Replace escaped dollars with literals
+inspection.can.convert.to.multi.dollar.string.display.name=Interpolation prefix can simplify string
+inspection.can.convert.to.multi.dollar.string.problem.description=An interpolation prefix can simplify the string
+add.interpolation.prefix=Add interpolation prefix
inspection.has.platform.type.display.name=Function or property has platform type
inspection.leaking.this.display.name=Leaking 'this' in constructor
inspection.redundant.if.display.name=Redundant 'if' statement
diff --git a/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html b/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html
new file mode 100644
index 000000000000..3e6a3b2a7485
--- /dev/null
+++ b/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html
@@ -0,0 +1,23 @@
+
+
+Reports that a string can benefit from a conversion to a version with an interpolation prefix.
+
+An interpolation prefix may make a string easier to comprehend, since prefixed strings can use dollar literals without additional escaping.
+
+Example:
+
+val jsonSchema: String = """
+{
+ "${'$'}id": "https://example.com/product.schema.json",
+}"""
+
+The quick fix converts all safely replaceable escaped dollar characters to regular dollar characters:
+
+val jsonSchema: String = $$"""
+{
+ "$id": "https://example.com/product.schema.json",
+}"""
+
+
+
+
\ No newline at end of file
diff --git a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt
index 83781086d763..8ffd831cef30 100644
--- a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt
+++ b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt
@@ -3,22 +3,13 @@
package org.jetbrains.kotlin.idea.codeinsights.impl.base
import com.intellij.openapi.util.text.StringUtil
-import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
-import org.jetbrains.kotlin.psi.KtEscapeStringTemplateEntry
-import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry
-import org.jetbrains.kotlin.psi.KtPsiFactory
-import org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry
-import org.jetbrains.kotlin.psi.KtStringTemplateEntry
-import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression
-import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted
import org.jetbrains.kotlin.psi.psiUtil.plainContent
private const val DEFAULT_INTERPOLATION_PREFIX_LENGTH: Int = 2
private const val INTERPOLATION_PREFIX_LENGTH_THRESHOLD: Int = 5
-private const val DOLLAR: String = "$"
-
val dollarLiteralExpressions: Array = arrayOf(
"'$'", "\"$\""
)
@@ -103,11 +94,11 @@ class EntryUpdateDiff(
}
}
-internal fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff {
+private fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff {
return EntryUpdateDiff(0.. 0) { "Unexpected string template prefix length: $prefixLength" }
val replacement = when (this) {
@@ -119,7 +110,7 @@ fun KtStringTemplateEntryWithExpression.changePrefixLength(prefixLength: Int): K
return replacement
}
-fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression {
+private fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression {
require(prefixLength > 0) { "Unexpected string template entry prefix length: $prefixLength" }
val ktPsiFactory = KtPsiFactory(project)
val expression = this.expression
@@ -138,7 +129,7 @@ fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTe
return replacement
}
-fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSimpleNameStringTemplateEntry {
+private fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSimpleNameStringTemplateEntry {
require(prefixLength > 0) { "Unexpected string template entry prefix length: $prefixLength" }
val ktPsiFactory = KtPsiFactory(project)
return ktPsiFactory.createMultiDollarSimpleNameStringTemplateEntry(
@@ -147,7 +138,7 @@ fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSim
)
}
-fun KtLiteralStringTemplateEntry.escapeIfNecessary(
+private fun KtLiteralStringTemplateEntry.escapeIfNecessary(
newPrefixLength: Int,
isSourceSingleQuoted: Boolean,
isDestinationSingleQuoted: Boolean,
@@ -183,7 +174,7 @@ private fun KtLiteralStringTemplateEntry.escapeDollarIfNecessary(
)
}
-fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List {
+private fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List {
val escaper = StringUtil.escaper(true, "\"")
var from = 0
var to = 0
@@ -216,7 +207,7 @@ fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List {
- return unescapedValue == "$"
- }
-
- is KtBlockStringTemplateEntry -> {
- expression?.text in dollarLiteralExpressions
- }
-
- else -> false
- }
+fun KtStringTemplateEntry.isEscapedDollar(): Boolean = when (this) {
+ is KtEscapeStringTemplateEntry -> this.isEscapedDollar()
+ is KtBlockStringTemplateEntry -> this.isInterpolatedDollarLiteralExpression()
+ else -> false
}
/**
- * Convert a string to a multi-dollar string, choosing an appropriate prefix length based on the string's content.
- * The function searches for the shortest possible prefix that doesn't exceed [INTERPOLATION_PREFIX_LENGTH_THRESHOLD].
- * If no such prefix exists, the [DEFAULT_INTERPOLATION_PREFIX_LENGTH] is used.
+ * Context for the multi-dollar conversion inspection and intention.
*/
-internal fun convertToMultiDollarString(element: KtStringTemplateExpression): KtStringTemplateExpression {
+class MultiDollarConversionInfo(
+ val prefixLength: Int,
+)
+
+fun prepareMultiDollarConversionInfo(element: KtStringTemplateExpression, useFallbackPrefix: Boolean): MultiDollarConversionInfo? {
+ val suitablePrefixLength = findSuitablePrefixLength(element, useFallbackPrefix) ?: return null
+ return MultiDollarConversionInfo(suitablePrefixLength)
+}
+
+/**
+ * Search for the shortest possible prefix that doesn't exceed [INTERPOLATION_PREFIX_LENGTH_THRESHOLD].
+ * If no such prefix exists, the [DEFAULT_INTERPOLATION_PREFIX_LENGTH] if [useFallbackPrefix] is `true`, or `null` otherwise.
+ */
+private fun findSuitablePrefixLength(element: KtStringTemplateExpression, useFallbackPrefix: Boolean): Int? {
+ val longestUnsafeDollarSequence = longestUnsafeDollarSequenceLength(element, INTERPOLATION_PREFIX_LENGTH_THRESHOLD)
+ if (longestUnsafeDollarSequence >= INTERPOLATION_PREFIX_LENGTH_THRESHOLD) {
+ return if (useFallbackPrefix) DEFAULT_INTERPOLATION_PREFIX_LENGTH else null
+ }
+ return maxOf(longestUnsafeDollarSequence + 1, DEFAULT_INTERPOLATION_PREFIX_LENGTH)
+}
+
+/**
+ * Convert a plain string to a multi-dollar string with the specified prefix length
+ */
+fun convertToMultiDollarString(element: KtStringTemplateExpression, contextInfo: MultiDollarConversionInfo): KtStringTemplateExpression {
require(element.interpolationPrefix == null) { "Can't convert the string which already has a prefix to multi-dollar string" }
-
- val longestUnsafeDollarSequence = longestUnsafeDollarSequenceLength(element, threshold = INTERPOLATION_PREFIX_LENGTH_THRESHOLD)
- val prefixLength = if (longestUnsafeDollarSequence in DEFAULT_INTERPOLATION_PREFIX_LENGTH..< INTERPOLATION_PREFIX_LENGTH_THRESHOLD)
- longestUnsafeDollarSequence + 1 else DEFAULT_INTERPOLATION_PREFIX_LENGTH
-
- replaceExpressionEntries(element, prefixLength)
+ replaceExpressionEntries(element, contextInfo.prefixLength)
val replaced = element.replace(
KtPsiFactory(element.project).createMultiDollarStringTemplate(
content = element.plainContent,
- prefixLength = prefixLength,
+ prefixLength = contextInfo.prefixLength,
forceMultiQuoted = !element.isSingleQuoted(),
)
) as KtStringTemplateExpression
@@ -306,7 +306,7 @@ internal fun convertToMultiDollarString(element: KtStringTemplateExpression): Kt
* Replace dollar escape sequences in a string template if it's safe, i.e., if replacement won't turn a literal part into interpolation.
* Both `\$` and `${'$'}` sequences are replaced if possible.
*/
-internal fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStringTemplateExpression {
+fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStringTemplateExpression {
val ktPsiFactory = KtPsiFactory(element.project)
val prefixLength = element.interpolationPrefix?.textLength?.takeIf { it > 1 } ?: return element
@@ -314,12 +314,12 @@ internal fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStrin
when (entry) {
is KtEscapeStringTemplateEntry -> {
if (entry.isEscapedDollar() && entry.isSafeToReplaceWithDollar(prefixLength))
- entry.replace(ktPsiFactory.createLiteralStringTemplateEntry(DOLLAR))
+ entry.replace(ktPsiFactory.createLiteralStringTemplateEntry("$"))
}
is KtBlockStringTemplateEntry -> {
if (entry.expression?.text in dollarLiteralExpressions && entry.isSafeToReplaceWithDollar(prefixLength))
- entry.replace(ktPsiFactory.createLiteralStringTemplateEntry(DOLLAR))
+ entry.replace(ktPsiFactory.createLiteralStringTemplateEntry("$"))
}
}
}
@@ -352,7 +352,7 @@ internal fun longestUnsafeDollarSequenceLength(
is KtBlockStringTemplateEntry -> {
when {
- entry.isSimplifiableInterpolatedDollar() -> current++
+ entry.isInterpolatedDollarLiteralExpression() -> current++
else -> {
current = 0
}
@@ -384,7 +384,7 @@ internal fun longestUnsafeDollarSequenceLength(
return longest
}
-private fun KtBlockStringTemplateEntry.isSimplifiableInterpolatedDollar(): Boolean {
+private fun KtBlockStringTemplateEntry.isInterpolatedDollarLiteralExpression(): Boolean {
return this.expression?.text in dollarLiteralExpressions
}
@@ -396,7 +396,7 @@ private fun replaceExpressionEntries(stringTemplate: KtStringTemplateExpression,
}
}
-private fun KtEscapeStringTemplateEntry.isEscapedDollar(): Boolean = unescapedValue == DOLLAR
+private fun KtEscapeStringTemplateEntry.isEscapedDollar(): Boolean = unescapedValue == "$"
/**
* It's unsafe to replace with a `$` if the part before the entry ends with a `$`, and the part after can be considered identifier/block.
@@ -406,6 +406,6 @@ private fun KtStringTemplateEntry.isSafeToReplaceWithDollar(prefixLength: Int):
if (prevSibling !is KtLiteralStringTemplateEntry) return true
val nextSiblingStringLiteral = nextSibling as? KtLiteralStringTemplateEntry ?: return true
if (!nextSiblingStringLiteral.canBeConsideredIdentifierOrBlock()) return true
- val trailingDollarsLength = prevSibling.text.takeLastWhile { it.toString() == DOLLAR }.length
+ val trailingDollarsLength = prevSibling.text.takeLastWhile { it.toString() == "$" }.length
return trailingDollarsLength + 1 < prefixLength
}
diff --git a/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml b/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml
index 9b5f6594013a..35746406c20f 100644
--- a/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml
+++ b/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml
@@ -68,6 +68,14 @@
language="kotlin"
key="inspection.can.unescape.dollar.literal.inspection.display.name" bundle="messages.KotlinBundle"/>
+
+
() {
+
+ override fun isApplicableByPsi(element: KtStringTemplateExpression): Boolean {
+ if (!element.languageVersionSettings.supportsFeature(LanguageFeature.MultiDollarInterpolation)) return false
+ return element.interpolationPrefix == null
+ }
+
+ override fun buildVisitor(
+ holder: ProblemsHolder,
+ isOnTheFly: Boolean
+ ): KtVisitor<*, *> {
+ return object : KtVisitorVoid() {
+ override fun visitStringTemplateExpression(entry: KtStringTemplateExpression) {
+ visitTargetElement(entry, holder, isOnTheFly)
+ }
+ }
+ }
+
+ context(KaSession)
+ override fun prepareContext(element: KtStringTemplateExpression): MultiDollarConversionInfo? {
+ if (!element.entries.any { it.isEscapedDollar() }) return null
+ return prepareMultiDollarConversionInfo(element, useFallbackPrefix = false)
+ }
+
+ override fun getProblemDescription(
+ element: KtStringTemplateExpression,
+ context: MultiDollarConversionInfo,
+ ): @InspectionMessage String {
+ return KotlinBundle.message("inspection.can.convert.to.multi.dollar.string.problem.description")
+ }
+
+ override fun createQuickFix(
+ element: KtStringTemplateExpression,
+ context: MultiDollarConversionInfo,
+ ): KotlinModCommandQuickFix {
+ return object : KotlinModCommandQuickFix() {
+ override fun getFamilyName(): @IntentionFamilyName String {
+ return KotlinBundle.message("add.interpolation.prefix")
+ }
+
+ override fun applyFix(
+ project: Project,
+ element: KtStringTemplateExpression,
+ updater: ModPsiUpdater
+ ) {
+ val multiDollarVersion = convertToMultiDollarString(element, context)
+ simplifyDollarEntries(multiDollarVersion)
+ }
+ }
+ }
+}
diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt
index fb1cdabf264b..53e9e43bc58c 100644
--- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt
+++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix
import org.jetbrains.kotlin.idea.codeinsights.impl.base.canBeStartOfIdentifierOrBlock
-import org.jetbrains.kotlin.idea.codeinsights.impl.base.dollarLiteralExpressions
import org.jetbrains.kotlin.idea.codeinsights.impl.base.isEscapedDollar
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted
diff --git a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java
index 46012113e308..d40d5e16c10d 100644
--- a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java
+++ b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java
@@ -8262,6 +8262,140 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
}
}
+ @RunWith(JUnit3RunnerWithInners.class)
+ @TestMetadata("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString")
+ public static class CanConvertToMultiDollarString extends AbstractK2LocalInspectionTest {
+ @java.lang.Override
+ @org.jetbrains.annotations.NotNull
+ public final KotlinPluginMode getPluginMode() {
+ return KotlinPluginMode.K2;
+ }
+
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("blockDollarWithChar.kt")
+ public void testBlockDollarWithChar() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt");
+ }
+
+ @TestMetadata("blockDollarWithString.kt")
+ public void testBlockDollarWithString() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt");
+ }
+
+ @TestMetadata("escapedDollar.kt")
+ public void testEscapedDollar() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt");
+ }
+
+ @TestMetadata("escapedDollarCaretInsideBlockExpression.kt")
+ public void testEscapedDollarCaretInsideBlockExpression() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt");
+ }
+
+ @TestMetadata("multipleSimplifiableDollars.kt")
+ public void testMultipleSimplifiableDollars() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt");
+ }
+
+ @TestMetadata("notReplaceableAlreadyPrefixed.kt")
+ public void testNotReplaceableAlreadyPrefixed() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt");
+ }
+
+ @TestMetadata("notReplaceableFirstIsTooLong.kt")
+ public void testNotReplaceableFirstIsTooLong() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt");
+ }
+
+ @TestMetadata("notReplaceableNothingToChange.kt")
+ public void testNotReplaceableNothingToChange() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt");
+ }
+
+ @TestMetadata("notReplaceableNothingToChangeTripleQuoted.kt")
+ public void testNotReplaceableNothingToChangeTripleQuoted() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt");
+ }
+
+ @TestMetadata("notReplaceableNothingToChangeTripleQuotedMultiline.kt")
+ public void testNotReplaceableNothingToChangeTripleQuotedMultiline() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt");
+ }
+
+ @TestMetadata("notReplaceableSecondIsTooLong.kt")
+ public void testNotReplaceableSecondIsTooLong() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt");
+ }
+
+ @TestMetadata("notReplaceableTooManyDollars.kt")
+ public void testNotReplaceableTooManyDollars() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt");
+ }
+
+ @TestMetadata("notReplaceableTooManyDollarsBacktickIdentifier.kt")
+ public void testNotReplaceableTooManyDollarsBacktickIdentifier() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt");
+ }
+
+ @TestMetadata("notReplaceableTooManyDollarsBlock.kt")
+ public void testNotReplaceableTooManyDollarsBlock() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt");
+ }
+
+ @TestMetadata("notReplaceableTooManyDollarsUnderscoreIdentifier.kt")
+ public void testNotReplaceableTooManyDollarsUnderscoreIdentifier() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt");
+ }
+
+ @TestMetadata("notReplaceableTripleQuotedMultiLine.kt")
+ public void testNotReplaceableTripleQuotedMultiLine() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt");
+ }
+
+ @TestMetadata("notReplaceableTripleQuotedPrefixed.kt")
+ public void testNotReplaceableTripleQuotedPrefixed() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt");
+ }
+
+ @TestMetadata("notReplaceableTripleQuotedSingleLine.kt")
+ public void testNotReplaceableTripleQuotedSingleLine() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt");
+ }
+
+ @TestMetadata("replaceableManyDollars.kt")
+ public void testReplaceableManyDollars() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt");
+ }
+
+ @TestMetadata("replaceableManyDollarsTwoSeqences.kt")
+ public void testReplaceableManyDollarsTwoSeqences() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt");
+ }
+
+ @TestMetadata("tripleQuotedReplaceableMultiLine.kt")
+ public void testTripleQuotedReplaceableMultiLine() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt");
+ }
+
+ @TestMetadata("tripleQuotedReplaceableMultiLineLong.kt")
+ public void testTripleQuotedReplaceableMultiLineLong() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt");
+ }
+
+ @TestMetadata("tripleQuotedReplaceableSingleLine.kt")
+ public void testTripleQuotedReplaceableSingleLine() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt");
+ }
+
+ @TestMetadata("tripleQuotedReplaceableSingleLineLong.kt")
+ public void testTripleQuotedReplaceableSingleLineLong() throws Exception {
+ runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt");
+ }
+ }
+
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/inspectionsLocal")
public abstract static class InspectionsLocal extends AbstractK2LocalInspectionTest {
diff --git a/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt
index 656b0aeed9e7..a94072e824a1 100644
--- a/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt
+++ b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt
@@ -10,13 +10,14 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinApplicableModCommandAction
-import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.multiDollarStrings.convertToMultiDollarString
-import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.multiDollarStrings.simplifyDollarEntries
+import org.jetbrains.kotlin.idea.codeinsights.impl.base.MultiDollarConversionInfo
+import org.jetbrains.kotlin.idea.codeinsights.impl.base.convertToMultiDollarString
+import org.jetbrains.kotlin.idea.codeinsights.impl.base.prepareMultiDollarConversionInfo
+import org.jetbrains.kotlin.idea.codeinsights.impl.base.simplifyDollarEntries
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
-internal class ConvertToMultiDollarStringIntention : KotlinApplicableModCommandAction(
- KtStringTemplateExpression::class,
-) {
+internal class ConvertToMultiDollarStringIntention :
+ KotlinApplicableModCommandAction(KtStringTemplateExpression::class) {
override fun getFamilyName(): @IntentionFamilyName String = KotlinBundle.message("convert.to.multi.dollar.string")
override fun isApplicableByPsi(element: KtStringTemplateExpression): Boolean {
@@ -27,13 +28,15 @@ internal class ConvertToMultiDollarStringIntention : KotlinApplicableModCommandA
override fun invoke(
actionContext: ActionContext,
element: KtStringTemplateExpression,
- elementContext: Unit,
+ elementContext: MultiDollarConversionInfo,
updater: ModPsiUpdater
) {
- val replaced = convertToMultiDollarString(element)
+ val replaced = convertToMultiDollarString(element, elementContext)
simplifyDollarEntries(replaced)
}
context(KaSession)
- override fun prepareContext(element: KtStringTemplateExpression): Unit = Unit
+ override fun prepareContext(element: KtStringTemplateExpression): MultiDollarConversionInfo? {
+ return prepareMultiDollarConversionInfo(element, useFallbackPrefix = true)
+ }
}
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection
new file mode 100644
index 000000000000..985d8d9bc6bc
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.k2.codeinsight.inspections.CanConvertToMultiDollarStringInspection
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt
new file mode 100644
index 000000000000..e638e168065b
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "${'$'}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after
new file mode 100644
index 000000000000..60624a2b4b67
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt
new file mode 100644
index 000000000000..89a8e3de833c
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "${"$"}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after
new file mode 100644
index 000000000000..60624a2b4b67
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt
new file mode 100644
index 000000000000..297fff59a129
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "\$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after
new file mode 100644
index 000000000000..60624a2b4b67
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt
new file mode 100644
index 000000000000..9f188b36c27b
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "\$ ${15 + 27}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after
new file mode 100644
index 000000000000..31a48768261d
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"$ $${15 + 27}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt
new file mode 100644
index 000000000000..2433400361a6
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "\$\$\$${'$'}${'$'}${"$"}${"$"}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after
new file mode 100644
index 000000000000..d9fb8ff2f887
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"$$$$$$$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt
new file mode 100644
index 000000000000..67e8aaa7f1bb
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ $$"\$"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt
new file mode 100644
index 000000000000..cade25e77457
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$\$\$\$Foo \$\$Bar"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt
new file mode 100644
index 000000000000..5dfb1f9b5763
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test(some: Int) {
+ "$10 10$ $$$$$ ${3 + 2} $some"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt
new file mode 100644
index 000000000000..ec7351034606
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test(some: Int) {
+ """$10 10$ $$$$$ ${3 + 2} $some"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt
new file mode 100644
index 000000000000..4d6e289c56a0
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt
@@ -0,0 +1,9 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test(some: Int) {
+ """
+ $10 10$ $$$$$ ${3 + 2} $some
+ $10 10$ $$$$$ ${3 + 2} $some
+ """.trimIndent()
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt
new file mode 100644
index 000000000000..2d4b5792cc63
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$Foo \$\$\$\$\$Bar"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt
new file mode 100644
index 000000000000..c72fb1c03dc1
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$\$\$\$Foo"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt
new file mode 100644
index 000000000000..60fa0278a097
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$\$\$\$`Foo`"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt
new file mode 100644
index 000000000000..cb4b37195a91
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$\$\$\${Foo}"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt
new file mode 100644
index 000000000000..3164f21c0a53
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ "\$\$\$\$\$_Foo"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt
new file mode 100644
index 000000000000..7852ca4e7373
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt
@@ -0,0 +1,10 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ """
+${'$'}${'$'}${'$'}${'$'}${'$'}Foo
+${'$'}${'$'}${'$'}${'$'}${'$'}Bar
+${'$'}${'$'}${'$'}${'$'}${'$'}Baz
+"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt
new file mode 100644
index 000000000000..ae1e45cd00a0
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ $$"""${'$'}${'$'}${'$'}Foo"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt
new file mode 100644
index 000000000000..886f4166fe1e
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt
@@ -0,0 +1,6 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+// PROBLEM: none
+
+fun test() {
+ """${'$'}${'$'}${'$'}${'$'}${'$'}Foo"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt
new file mode 100644
index 000000000000..5085c1ee98ec
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "${'$'}\$\$${"$"}Foo"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after
new file mode 100644
index 000000000000..a5a865b28374
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$$$$"$$$$Foo"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt
new file mode 100644
index 000000000000..aa35c5d86fc3
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ "${'$'}\$\$${"$"}Foo ${'$'}\$\$${"$"}Bar"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after
new file mode 100644
index 000000000000..bb4d3b4f5145
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$$$$"$$$$Foo $$$$Bar"
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt
new file mode 100644
index 000000000000..a2f2fc49ac46
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt
@@ -0,0 +1,9 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ """
+\$\$${'$'}${'$'}${'$'}Foo
+\$\$${'$'}${'$'}${'$'}Bar
+\$\$${'$'}${'$'}${'$'}Baz
+"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after
new file mode 100644
index 000000000000..2eb8ce58a59f
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after
@@ -0,0 +1,9 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$$$$"""
+\$\$$$$Foo
+\$\$$$$Bar
+\$\$$$$Baz
+"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt
new file mode 100644
index 000000000000..b29d9ad84073
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt
@@ -0,0 +1,9 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ """
+${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}
+${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}
+${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}
+"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after
new file mode 100644
index 000000000000..db8efceca7bb
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after
@@ -0,0 +1,9 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"""
+$$$$$$
+$$$$$$
+$$$$$$
+"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt
new file mode 100644
index 000000000000..a9824f8525c1
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ """\$\$${'$'}${'$'}${'$'}Foo"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after
new file mode 100644
index 000000000000..ccb040be3136
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$$$$"""\$\$$$$Foo"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt
new file mode 100644
index 000000000000..2b082b042c47
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ """${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after
new file mode 100644
index 000000000000..9b579d274e88
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after
@@ -0,0 +1,5 @@
+// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
+
+fun test() {
+ $$"""$$$$$$"""
+}
\ No newline at end of file
diff --git a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
index d968f4498bcb..1950240dd1f4 100644
--- a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
+++ b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
@@ -589,6 +589,7 @@ private fun assembleWorkspace(): TWorkspace = workspace(KotlinPluginMode.K1) {
excludedDirectories = listOf(
"unusedVariable", // In FE1.0, this is a quickfix rather than a local inspection
"canSimplifyDollarLiteral", // K2-only
+ "canConvertToMultiDollarString", // K2-only
)
)
}
diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt
index 0e9a1f87ab06..eab699ea9678 100644
--- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt
+++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt
@@ -66,6 +66,7 @@ internal fun MutableTWorkspace.generateK2InspectionTests() {
model("${idea}/inspectionsLocal/arrayInDataClass")
model("${idea}/inspectionsLocal/collections/simplifiableCallChain")
model("${idea}/inspectionsLocal/canSimplifyDollarLiteral")
+ model("${idea}/inspectionsLocal/canConvertToMultiDollarString")
model("code-insight/inspections-k2/tests/testData/inspectionsLocal", pattern = pattern)
}
/**