@@ -41,6 +41,7 @@ import com.pinterest.ktlint.core.ast.ElementType.RBRACE
41
41
import com.pinterest.ktlint.core.ast.ElementType.RBRACKET
42
42
import com.pinterest.ktlint.core.ast.ElementType.RPAR
43
43
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
44
+ import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY
44
45
import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
45
46
import com.pinterest.ktlint.core.ast.ElementType.THEN
46
47
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
@@ -59,6 +60,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
59
60
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
60
61
import org.jetbrains.kotlin.psi.psiUtil.startOffset
61
62
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
63
+ import java.lang.StringBuilder
62
64
import kotlin.math.abs
63
65
64
66
/* *
@@ -196,7 +198,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
196
198
if (checkResult?.isCorrect != true && expectedIndent != indentError.actual) {
197
199
WRONG_INDENTATION .warnAndFix(configRules, emitWarn, isFixMode, " expected $expectedIndent but was ${indentError.actual} " ,
198
200
whiteSpace.startOffset + whiteSpace.text.lastIndexOf(' \n ' ) + 1 , whiteSpace.node) {
199
- checkStringLiteral(whiteSpace, expectedIndent)
201
+ checkStringLiteral(whiteSpace, expectedIndent, indentError.actual )
200
202
whiteSpace.node.indentBy(expectedIndent)
201
203
}
202
204
}
@@ -205,7 +207,11 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
205
207
/* *
206
208
* Checks if it is triple-quoted string template with trimIndent() or trimMargin() function.
207
209
*/
208
- private fun checkStringLiteral (whiteSpace : PsiWhiteSpace , expectedIndent : Int ) {
210
+ private fun checkStringLiteral (
211
+ whiteSpace : PsiWhiteSpace ,
212
+ expectedIndent : Int ,
213
+ actualIndent : Int
214
+ ) {
209
215
val nextNode = whiteSpace.node.treeNext
210
216
if (nextNode != null &&
211
217
nextNode.elementType == DOT_QUALIFIED_EXPRESSION &&
@@ -215,19 +221,23 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
215
221
it == " trimIndent()" ||
216
222
it == " trimMargin()"
217
223
} == true ) {
218
- fixStringLiteral(whiteSpace, expectedIndent)
224
+ fixStringLiteral(whiteSpace, expectedIndent, actualIndent )
219
225
}
220
226
}
221
227
222
228
/* *
223
229
* If it is triple-quoted string template we need to indent all its parts
224
230
*/
225
- private fun fixStringLiteral (whiteSpace : PsiWhiteSpace , expectedIndent : Int ) {
231
+ private fun fixStringLiteral (
232
+ whiteSpace : PsiWhiteSpace ,
233
+ expectedIndent : Int ,
234
+ actualIndent : Int
235
+ ) {
226
236
val textIndent = " " .repeat(expectedIndent + INDENT_SIZE )
227
237
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 )
231
241
}
232
242
}
233
243
(templateEntries.last().firstChildNode as LeafPsiElement )
@@ -238,12 +248,43 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
238
248
.trim())
239
249
}
240
250
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
+
241
283
private fun ASTNode.getExceptionalIndentInitiator () = treeParent.let { parent ->
242
284
when (parent.psi) {
243
285
// 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()
247
288
is KtIfExpression -> parent.findChildByType(THEN ) ? : parent.findChildByType(ELSE ) ? : parent
248
289
is KtLoopExpression -> (parent.psi as KtLoopExpression ).body?.node ? : parent
249
290
else -> parent
@@ -340,6 +381,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
340
381
private val increasingTokens = listOf (LPAR , LBRACE , LBRACKET , LONG_TEMPLATE_ENTRY_START )
341
382
private val decreasingTokens = listOf (RPAR , RBRACE , RBRACKET , LONG_TEMPLATE_ENTRY_END )
342
383
private val matchingTokens = increasingTokens.zip(decreasingTokens)
384
+ private val stringLiteralTokens = listOf (SHORT_STRING_TEMPLATE_ENTRY , LONG_STRING_TEMPLATE_ENTRY )
343
385
}
344
386
}
345
387
0 commit comments