Skip to content

Commit

Permalink
Do not merge opening quotes of multiline string template with (single…
Browse files Browse the repository at this point in the history
… line) function signature (#2609)

Closes #2592
  • Loading branch information
paul-dingemans committed Mar 15, 2024
1 parent d429142 commit afd32bc
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 8 deletions.
@@ -1,5 +1,6 @@
package com.pinterest.ktlint.ruleset.standard.rules

import com.pinterest.ktlint.rule.engine.core.api.ElementType
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY
Expand Down Expand Up @@ -33,6 +34,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPE
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY_OFF
import com.pinterest.ktlint.rule.engine.core.api.indent
import com.pinterest.ktlint.rule.engine.core.api.isCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf
Expand Down Expand Up @@ -563,14 +565,16 @@ public class FunctionSignatureRule :
return
}
val mergeWithFunctionSignature =
if (firstLineOfBodyExpression.length < maxLengthRemainingForFirstLineOfBodyExpression) {
functionBodyExpressionWrapping == default ||
(functionBodyExpressionWrapping == multiline && functionBodyExpressionLines.size == 1) ||
node.isMultilineFunctionSignatureWithoutExplicitReturnType(
lastNodeOfFunctionSignatureWithBodyExpression,
)
} else {
false
when {
firstLineOfBodyExpression.length < maxLengthRemainingForFirstLineOfBodyExpression -> {
(functionBodyExpressionWrapping == default && !functionBodyExpressionNodes.isMultilineStringTemplate()) ||
(functionBodyExpressionWrapping == multiline && functionBodyExpressionLines.size == 1) ||
node.isMultilineFunctionSignatureWithoutExplicitReturnType(
lastNodeOfFunctionSignatureWithBodyExpression,
)
}

else -> false
}
if (mergeWithFunctionSignature) {
emit(
Expand Down Expand Up @@ -621,6 +625,17 @@ public class FunctionSignatureRule :
}
}

private fun List<ASTNode>.isMultilineStringTemplate() =
first { it.isCodeLeaf() }
.let {
it.elementType == ElementType.OPEN_QUOTE &&
it
.nextLeaf()
?.text
.orEmpty()
.startsWith("\n")
}

private fun ASTNode.isMultilineFunctionSignatureWithoutExplicitReturnType(lastNodeOfFunctionSignatureWithBodyExpression: ASTNode?) =
functionSignatureNodes()
.childrenBetween(
Expand Down
Expand Up @@ -13,6 +13,7 @@ import com.pinterest.ktlint.test.KtLintAssertThat.Companion.EOL_CHAR
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.MAX_LINE_LENGTH_MARKER
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRuleBuilder
import com.pinterest.ktlint.test.LintViolation
import com.pinterest.ktlint.test.MULTILINE_STRING_QUOTE
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.jupiter.api.DisplayName
Expand Down Expand Up @@ -1313,6 +1314,56 @@ class FunctionSignatureRuleTest {
.hasNoLintViolations()
}

@Test
fun `Issue 2592 - Given a function signature with an expression body that is multiline raw string literal then do not join the first leaf with the function signature`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo1(): String =
$MULTILINE_STRING_QUOTE
some text
$MULTILINE_STRING_QUOTE.trimIndent()
fun foo2() =
$MULTILINE_STRING_QUOTE
some text
$MULTILINE_STRING_QUOTE.trimIndent()
""".trimIndent()
functionSignatureWrappingRuleAssertThat(code)
.setMaxLineLength()
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.hasNoLintViolations()
}

@Test
fun `Issue 2592 - Given a multiline function signature with an expression body that is multiline raw string literal then do join the first leaf with the function signature`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
foo: Foo,
bar: Bar,
) =
$MULTILINE_STRING_QUOTE
some text
$MULTILINE_STRING_QUOTE.trimIndent()
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
foo: Foo,
bar: Bar,
) = $MULTILINE_STRING_QUOTE
some text
$MULTILINE_STRING_QUOTE.trimIndent()
""".trimIndent()
functionSignatureWrappingRuleAssertThat(code)
.setMaxLineLength()
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.hasLintViolation(5, 4, "First line of body expression fits on same line as function signature")
.isFormattedAs(formattedCode)
}

private companion object {
const val UNEXPECTED_SPACES = " "
const val NO_SPACE = ""
Expand Down

0 comments on commit afd32bc

Please sign in to comment.