"Join lines" works incorrectly in case of line with more than one string literal (KT-22374)

#KT-22374 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-01-22 17:42:06 +09:00
committed by Nikolay Krasko
parent c06aaf6128
commit 4232ad8dfe
7 changed files with 88 additions and 15 deletions
@@ -57,7 +57,7 @@ open class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentInte
}
@JvmStatic
fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
protected fun buildReplacement(expression: KtBinaryExpression): KtStringTemplateExpression {
val rightText = buildText(expression.right, false)
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 ""
val expression = KtPsiUtil.safeDeparenthesize(expr)
val expressionText = expression.text
@@ -130,7 +130,7 @@ open class ConvertToStringTemplateIntention : SelfTargetingOffsetIndependentInte
return "\${$expressionText}"
}
fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
private fun isApplicableToNoParentCheck(expression: KtBinaryExpression): Boolean {
if (expression.operationToken != KtTokens.PLUS) return false
val expressionType = expression.analyze(BodyResolveMode.PARTIAL).getType(expression)
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.psi.PsiFile
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.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
class JoinToStringTemplateHandler : JoinRawLinesHandlerDelegate {
@@ -35,16 +35,61 @@ class JoinToStringTemplateHandler : JoinRawLinesHandlerDelegate {
val index = if (c == '\n') start - 1 else start
val plus = file.findElementAt(index)?.takeIf { it.node?.elementType == KtTokens.PLUS } ?: return -1
val expression = (plus.parent.parent as? KtBinaryExpression) ?: return -1
val left = expression.left ?: return -1
val right = expression.right ?: return -1
if (left !is KtStringTemplateExpression || right !is KtStringTemplateExpression) return -1
if (!ConvertToStringTemplateIntention.isApplicableToNoParentCheck(expression)) return -1
var binaryExpr = (plus.parent.parent as? KtBinaryExpression) ?: return -1
if (!binaryExpr.joinable()) return -1
val offset = left.endOffset - 1
expression.replace(ConvertToStringTemplateIntention.buildReplacement(expression))
return offset
val lineCount = binaryExpr.getLineCount()
val nextLineCount = lineCount + 1
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
}
}
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"
@@ -0,0 +1,5 @@
val foo = "test"
val s = "1" + "2" +
foo + "4" +<caret>
"5" + foo +
"7" + "8"
@@ -0,0 +1,4 @@
val foo = "test"
val s = "1" + "2" +
foo + "4<caret>5" + foo +
"7" + "8"
@@ -429,6 +429,18 @@ public class JoinLinesTestGenerated extends AbstractJoinLinesTest {
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")
public void testSecondLineVariable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/stringTemplate/secondLineVariable.kt");