"Join lines" works incorrectly in case of line with more than one string literal (KT-22374)
#KT-22374 Fixed
This commit is contained in:
committed by
Nikolay Krasko
parent
c06aaf6128
commit
4232ad8dfe
@@ -57,7 +57,7 @@ open class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
|
protected fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
|
||||||
val rightText = buildText(expression.right, false)
|
val rightText = buildText(expression.right, false)
|
||||||
return fold(expression.left, rightText, KtPsiFactory(expression))
|
return fold(expression.left, rightText, KtPsiFactory(expression))
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ open class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentInte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildText(expr: KtExpression?, forceBraces: Boolean): String {
|
fun buildText(expr: KtExpression?, forceBraces: Boolean): String {
|
||||||
if (expr == null) return ""
|
if (expr == null) return ""
|
||||||
val expression = KtPsiUtil.safeDeparenthesize(expr)
|
val expression = KtPsiUtil.safeDeparenthesize(expr)
|
||||||
val expressionText = expression.text
|
val expressionText = expression.text
|
||||||
@@ -130,7 +130,7 @@ open class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentInte
|
|||||||
return "\${$expressionText}"
|
return "\${$expressionText}"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
|
private fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
|
||||||
if (expression.operationToken != KtTokens.PLUS) return false
|
if (expression.operationToken != KtTokens.PLUS) return false
|
||||||
val expressionType = expression.analyze(BodyResolveMode.PARTIAL).getType(expression)
|
val expressionType = expression.analyze(BodyResolveMode.PARTIAL).getType(expression)
|
||||||
if (!KotlinBuiltIns.isString(expressionType)) return false
|
if (!KotlinBuiltIns.isString(expressionType)) return false
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ import com.intellij.codeInsight.editorActions.JoinRawLinesHandlerDelegate
|
|||||||
import com.intellij.openapi.editor.Document
|
import com.intellij.openapi.editor.Document
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateIntention
|
import org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateIntention
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.lineCount
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.getLineCount
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
|
||||||
class JoinToStringTemplateHandler : JoinRawLinesHandlerDelegate {
|
class JoinToStringTemplateHandler : JoinRawLinesHandlerDelegate {
|
||||||
@@ -35,16 +35,61 @@ class JoinToStringTemplateHandler : JoinRawLinesHandlerDelegate {
|
|||||||
val index = if (c == '\n') start - 1 else start
|
val index = if (c == '\n') start - 1 else start
|
||||||
|
|
||||||
val plus = file.findElementAt(index)?.takeIf { it.node?.elementType == KtTokens.PLUS } ?: return -1
|
val plus = file.findElementAt(index)?.takeIf { it.node?.elementType == KtTokens.PLUS } ?: return -1
|
||||||
val expression = (plus.parent.parent as? KtBinaryExpression) ?: return -1
|
var binaryExpr = (plus.parent.parent as? KtBinaryExpression) ?: return -1
|
||||||
val left = expression.left ?: return -1
|
if (!binaryExpr.joinable()) return -1
|
||||||
val right = expression.right ?: return -1
|
|
||||||
if (left !is KtStringTemplateExpression || right !is KtStringTemplateExpression) return -1
|
|
||||||
if (!ConvertToStringTemplateIntention.isApplicableToNoParentCheck(expression)) return -1
|
|
||||||
|
|
||||||
val offset = left.endOffset - 1
|
val lineCount = binaryExpr.getLineCount()
|
||||||
expression.replace(ConvertToStringTemplateIntention.buildReplacement(expression))
|
val nextLineCount = lineCount + 1
|
||||||
return offset
|
|
||||||
|
var parent = binaryExpr.parent
|
||||||
|
while (parent is KtBinaryExpression && parent.joinable() && parent.lineCount() == nextLineCount) {
|
||||||
|
binaryExpr = parent
|
||||||
|
parent = parent.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
var rightText = ConvertToStringTemplateIntention.buildText(binaryExpr.right, false)
|
||||||
|
var left = binaryExpr.left
|
||||||
|
while (left is KtBinaryExpression && left.joinable() && left.parent.getLineCount() >= lineCount) {
|
||||||
|
rightText = ConvertToStringTemplateIntention.buildText(left.right, false) + rightText
|
||||||
|
left = left.left
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (left) {
|
||||||
|
is KtStringTemplateExpression -> {
|
||||||
|
val offset = left.endOffset - 1
|
||||||
|
binaryExpr.replace(createStringTemplate(left, rightText))
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
is KtBinaryExpression -> {
|
||||||
|
val leftRight = left.right
|
||||||
|
if (leftRight is KtStringTemplateExpression) {
|
||||||
|
val offset = leftRight.endOffset - 1
|
||||||
|
leftRight.replace(createStringTemplate(leftRight, rightText))
|
||||||
|
binaryExpr.replace(left)
|
||||||
|
offset
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStringTemplate(left: KtStringTemplateExpression, rightText: String): KtStringTemplateExpression {
|
||||||
|
val leftText = ConvertToStringTemplateIntention.buildText(left, false)
|
||||||
|
return KtPsiFactory(left).createExpression("\"$leftText$rightText\"") as KtStringTemplateExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tryJoinLines(document: Document, file: PsiFile, start: Int, end: Int): Int = -1
|
override fun tryJoinLines(document: Document, file: PsiFile, start: Int, end: Int): Int = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtBinaryExpression.joinable(): Boolean {
|
||||||
|
if (operationToken != KtTokens.PLUS) return false
|
||||||
|
if (right !is KtStringTemplateExpression) return false
|
||||||
|
val left = left
|
||||||
|
return when (left) {
|
||||||
|
is KtStringTemplateExpression -> true
|
||||||
|
is KtBinaryExpression -> left.right is KtStringTemplateExpression
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
val s = "1" + "2" +
|
||||||
|
"3" + "4" +<caret>
|
||||||
|
"5" + "6" +
|
||||||
|
"7" + "8"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
val s = "1" + "2" +
|
||||||
|
"3<caret>456" +
|
||||||
|
"7" + "8"
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
val foo = "test"
|
||||||
|
val s = "1" + "2" +
|
||||||
|
foo + "4" +<caret>
|
||||||
|
"5" + foo +
|
||||||
|
"7" + "8"
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
val foo = "test"
|
||||||
|
val s = "1" + "2" +
|
||||||
|
foo + "4<caret>5" + foo +
|
||||||
|
"7" + "8"
|
||||||
+12
@@ -429,6 +429,18 @@ public class JoinLinesTestGenerated extends AbstractJoinLinesTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lineWithMultiStringLiteral.kt")
|
||||||
|
public void testLineWithMultiStringLiteral() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/stringTemplate/lineWithMultiStringLiteral.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lineWithMultiStringLiteralAndVariable.kt")
|
||||||
|
public void testLineWithMultiStringLiteralAndVariable() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/stringTemplate/lineWithMultiStringLiteralAndVariable.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("secondLineVariable.kt")
|
@TestMetadata("secondLineVariable.kt")
|
||||||
public void testSecondLineVariable() throws Exception {
|
public void testSecondLineVariable() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/stringTemplate/secondLineVariable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/stringTemplate/secondLineVariable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user