Skip to content

Commit 8891a13

Browse files
authored
Bugfix. Bug in autofix in string templates (#780)
### What's done: * Fixed bug
1 parent fbb6a20 commit 8891a13

File tree

5 files changed

+101
-10
lines changed

5 files changed

+101
-10
lines changed

diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import com.pinterest.ktlint.core.ast.ElementType.RBRACE
4141
import com.pinterest.ktlint.core.ast.ElementType.RBRACKET
4242
import com.pinterest.ktlint.core.ast.ElementType.RPAR
4343
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
44+
import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY
4445
import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
4546
import com.pinterest.ktlint.core.ast.ElementType.THEN
4647
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
@@ -59,6 +60,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
5960
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
6061
import org.jetbrains.kotlin.psi.psiUtil.startOffset
6162
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
63+
import java.lang.StringBuilder
6264
import kotlin.math.abs
6365

6466
/**
@@ -196,7 +198,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
196198
if (checkResult?.isCorrect != true && expectedIndent != indentError.actual) {
197199
WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "expected $expectedIndent but was ${indentError.actual}",
198200
whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1, whiteSpace.node) {
199-
checkStringLiteral(whiteSpace, expectedIndent)
201+
checkStringLiteral(whiteSpace, expectedIndent, indentError.actual)
200202
whiteSpace.node.indentBy(expectedIndent)
201203
}
202204
}
@@ -205,7 +207,11 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
205207
/**
206208
* Checks if it is triple-quoted string template with trimIndent() or trimMargin() function.
207209
*/
208-
private fun checkStringLiteral(whiteSpace: PsiWhiteSpace, expectedIndent: Int) {
210+
private fun checkStringLiteral(
211+
whiteSpace: PsiWhiteSpace,
212+
expectedIndent: Int,
213+
actualIndent: Int
214+
) {
209215
val nextNode = whiteSpace.node.treeNext
210216
if (nextNode != null &&
211217
nextNode.elementType == DOT_QUALIFIED_EXPRESSION &&
@@ -215,19 +221,23 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
215221
it == "trimIndent()" ||
216222
it == "trimMargin()"
217223
} == true) {
218-
fixStringLiteral(whiteSpace, expectedIndent)
224+
fixStringLiteral(whiteSpace, expectedIndent, actualIndent)
219225
}
220226
}
221227

222228
/**
223229
* If it is triple-quoted string template we need to indent all its parts
224230
*/
225-
private fun fixStringLiteral(whiteSpace: PsiWhiteSpace, expectedIndent: Int) {
231+
private fun fixStringLiteral(
232+
whiteSpace: PsiWhiteSpace,
233+
expectedIndent: Int,
234+
actualIndent: Int
235+
) {
226236
val textIndent = " ".repeat(expectedIndent + INDENT_SIZE)
227237
val templateEntries = whiteSpace.node.treeNext.firstChildNode.getAllChildrenWithType(LITERAL_STRING_TEMPLATE_ENTRY)
228-
templateEntries.forEach {
229-
if (!it.text.contains("\n") && it.text.isNotBlank()) {
230-
(it.firstChildNode as LeafPsiElement).rawReplaceWithText(textIndent + it.firstChildNode.text.trim())
238+
templateEntries.forEach { node ->
239+
if (!node.text.contains("\n")) {
240+
fixFirstTemplateEntries(node, textIndent, actualIndent)
231241
}
232242
}
233243
(templateEntries.last().firstChildNode as LeafPsiElement)
@@ -238,12 +248,43 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
238248
.trim())
239249
}
240250

251+
/**
252+
* This method fixes all lines of string template except the last one
253+
* Also it considers $foo insertions in string
254+
*/
255+
private fun fixFirstTemplateEntries(
256+
node: ASTNode,
257+
textIndent: String,
258+
actualIndent: Int
259+
) {
260+
val correctedText = StringBuilder()
261+
// shift of the node depending on its initial string template indent
262+
val nodeStartIndent = if (node.firstChildNode.text.takeWhile { it == ' ' }.count() - actualIndent - INDENT_SIZE > 0) {
263+
node.firstChildNode.text.takeWhile { it == ' ' }.count() - actualIndent - INDENT_SIZE
264+
} else {
265+
0
266+
}
267+
val isPrevStringTemplate = node.treePrev.elementType in stringLiteralTokens
268+
val isNextStringTemplate = node.treeNext.elementType in stringLiteralTokens
269+
when {
270+
// if string template is before literal_string
271+
isPrevStringTemplate && !isNextStringTemplate -> correctedText.append(node.firstChildNode.text.trimEnd())
272+
// if string template is after literal_string
273+
!isPrevStringTemplate && isNextStringTemplate -> correctedText.append(textIndent + " ".repeat(nodeStartIndent) + node.firstChildNode.text.trimStart())
274+
// if there is no string template in literal_string
275+
!isPrevStringTemplate && !isNextStringTemplate -> correctedText.append(textIndent + " ".repeat(nodeStartIndent) + node.firstChildNode.text.trim())
276+
isPrevStringTemplate && isNextStringTemplate -> correctedText.append(node.firstChildNode.text)
277+
node.text.isBlank() -> correctedText.append(textIndent)
278+
else -> {}
279+
}
280+
(node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctedText.toString())
281+
}
282+
241283
private fun ASTNode.getExceptionalIndentInitiator() = treeParent.let { parent ->
242284
when (parent.psi) {
243285
// fixme: custom logic for determining exceptional indent initiator, should be moved elsewhere
244-
is KtDotQualifiedExpression ->
245-
// get the topmost expression to keep extended indent for the whole chain of dot call expressions
246-
parents().takeWhile { it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION }.last()
286+
// get the topmost expression to keep extended indent for the whole chain of dot call expressions
287+
is KtDotQualifiedExpression -> parents().takeWhile { it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION }.last()
247288
is KtIfExpression -> parent.findChildByType(THEN) ?: parent.findChildByType(ELSE) ?: parent
248289
is KtLoopExpression -> (parent.psi as KtLoopExpression).body?.node ?: parent
249290
else -> parent
@@ -340,6 +381,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
340381
private val increasingTokens = listOf(LPAR, LBRACE, LBRACKET, LONG_TEMPLATE_ENTRY_START)
341382
private val decreasingTokens = listOf(RPAR, RBRACE, RBRACKET, LONG_TEMPLATE_ENTRY_END)
342383
private val matchingTokens = increasingTokens.zip(decreasingTokens)
384+
private val stringLiteralTokens = listOf(SHORT_STRING_TEMPLATE_ENTRY, LONG_STRING_TEMPLATE_ENTRY)
343385
}
344386
}
345387

diktat-rules/src/test/resources/test/paragraph3/indentation/IndentationFull1Expected.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,23 @@ data class Example(val field1: Type1,
4646
.bar()
4747
}"
4848
}
49+
50+
val dockerFileAsText =
51+
"""
52+
FROM $baseImage
53+
COPY resources $resourcesPath
54+
RUN /bin/bash
55+
""".trimIndent()
56+
57+
val some =
58+
"""
59+
some $foo test
60+
$start another value
61+
""".trimIndent()
62+
63+
val teeest =
64+
"""
65+
some text $foo $bar another text
66+
""".trimIndent()
4967
}
5068

diktat-rules/src/test/resources/test/paragraph3/indentation/IndentationFull1Test.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,22 @@ fun foo(
4646
.bar()
4747
}"
4848
}
49+
50+
val dockerFileAsText =
51+
"""
52+
FROM $baseImage
53+
COPY resources $resourcesPath
54+
RUN /bin/bash
55+
""".trimIndent()
56+
57+
val some =
58+
"""
59+
some $foo test
60+
$start another value
61+
""".trimIndent()
62+
63+
val teeest =
64+
"""
65+
some text $foo $bar another text
66+
""".trimIndent()
4967
}

diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@ val text =
1818
x
1919
"""
2020

21+
val dockerFileAsText =
22+
"""
23+
FROM $baseImage someTest
24+
COPY resources $resourcesPath
25+
RUN /bin/bash
26+
""".trimIndent() // RUN command shouldn't matter because it will be replaced on container creation
27+

diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@ val text =
1818
x
1919
"""
2020

21+
val dockerFileAsText =
22+
"""
23+
FROM $baseImage someTest
24+
COPY resources $resourcesPath
25+
RUN /bin/bash
26+
""".trimIndent() // RUN command shouldn't matter because it will be replaced on container creation

0 commit comments

Comments
 (0)