Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge first line of body expression with function signature #1566

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ The callback function provided as parameter to the format function is now called
* When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory ([#1533](https://github.com/pinterest/ktlint/issue/1533)).
* Execute `ktlint` cli on default kotlin extensions only when an (existing) path to a directory is given. ([#917](https://github.com/pinterest/ktlint/issue/917)).
* Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491))
* Merge first line of body expression with function signature only when it fits on the same line `function-signature` ([#1527](https://github.com/pinterest/ktlint/issues/1527))
* Add missing whitespace when else is on same line as true condition `multiline-if-else` ([#1560](https://github.com/pinterest/ktlint/issues/1560))
* Fix multiline if-statements `multiline-if-else` ([#828](https://github.com/pinterest/ktlint/issues/828))
* Prevent class cast exception on ".editorconfig" property `ktlint_code_style` ([#1559](https://github.com/pinterest/ktlint/issues/1559))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,11 @@ public class FunctionSignatureRule :
.firstOrNull()
?.also { firstLineOfBodyExpression ->
if (whiteSpaceBeforeFunctionBodyExpression.isWhiteSpaceWithNewline()) {
if (functionBodyExpressionWrapping == default ||
(functionBodyExpressionWrapping == multiline && functionBodyExpressionLines.size == 1) ||
val mergeWithFunctionSignature =
functionBodyExpressionWrapping.keepFirstLineOfBodyExpressionTogetherWithFunctionSignature(
firstLineOfBodyExpression.length < maxLengthRemainingForFirstLineOfBodyExpression
)
if (mergeWithFunctionSignature ||
node.isMultilineFunctionSignatureWithoutExplicitReturnType(lastNodeOfFunctionSignatureWithBodyExpression)
) {
emit(
Expand Down Expand Up @@ -754,5 +757,12 @@ public class FunctionSignatureRule :
* Always force the body expression to start on a separate line.
*/
always;

internal fun keepFirstLineOfBodyExpressionTogetherWithFunctionSignature(fitOnSameLine: Boolean) =
if (this == default || this == multiline) {
fitOnSameLine
} else {
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -786,12 +786,46 @@ class FunctionSignatureRuleTest {
).isFormattedAs(formattedCode)
}

@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionSignatureRule.FunctionBodyExpressionWrapping::class,
names = ["default", "multiline", "always"]
)
fun `Given that the function signature and first line of a multi line body expression body do not fit on the same line then do reformat`(
bodyExpressionWrapping: FunctionSignatureRule.FunctionBodyExpressionWrapping
) {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun f(
a: Any,
b: Any
): String = "some-result"
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun f(a: Any, b: Any): String =
"some-result"
""".trimIndent()
functionSignatureWrappingRuleAssertThat(code)
.setMaxLineLength()
.withEditorConfigOverride(functionBodyExpressionWrappingProperty to bodyExpressionWrapping)
.addAdditionalRuleProvider { IndentationRule() }
.hasLintViolations(
LintViolation(3, 5, "No whitespace expected between opening parenthesis and first parameter name"),
LintViolation(4, 5, "Single whitespace expected before parameter"),
LintViolation(4, 11, "No whitespace expected between last parameter and closing parenthesis"),
LintViolation(5, 13, "Newline expected before expression body")
).isFormattedAs(formattedCode)
}

@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionSignatureRule.FunctionBodyExpressionWrapping::class,
names = ["always"]
)
fun `Given that the function signature and a single line body expression body fit on the same line then reformat to single line signature but keep body expression on a separate line`(
fun `Given that the function signature and the first line of a multi line body expression body fit on the same line then reformat to single line signature but keep body expression on separate line`(
bodyExpressionWrapping: FunctionSignatureRule.FunctionBodyExpressionWrapping
) {
val code =
Expand All @@ -802,12 +836,14 @@ class FunctionSignatureRuleTest {
b: Any
): String =
"some-result"
.trim()
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun f(a: Any, b: Any): String =
"some-result"
.trim()
""".trimIndent()
functionSignatureWrappingRuleAssertThat(code)
.setMaxLineLength()
Expand All @@ -823,7 +859,7 @@ class FunctionSignatureRuleTest {
@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionSignatureRule.FunctionBodyExpressionWrapping::class,
names = ["default"]
names = ["default", "multiline"]
)
fun `Given that the function signature and first line of a multiline body expression body fit on the same line then do reformat as single line signature`(
bodyExpressionWrapping: FunctionSignatureRule.FunctionBodyExpressionWrapping
Expand Down Expand Up @@ -859,7 +895,7 @@ class FunctionSignatureRuleTest {
@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionSignatureRule.FunctionBodyExpressionWrapping::class,
names = ["multiline", "always"]
names = ["always"]
)
fun `Given that the function signature and first line of a multiline body expression body fit on the same line then do reformat as single line signature, keep the body expression on a separate line`(
bodyExpressionWrapping: FunctionSignatureRule.FunctionBodyExpressionWrapping
Expand Down Expand Up @@ -897,7 +933,7 @@ class FunctionSignatureRuleTest {
@EnumSource(
value = FunctionSignatureRule.FunctionBodyExpressionWrapping::class
)
fun `Given a multiline function signature without explicit return type and start of body expression on next line then keep first line of body expression body on the same line as the last line og the function signature`(
fun `Given a multiline function signature without explicit return type and start of body expression on next line then keep first line of body expression body on the same line as the last line of the function signature`(
bodyExpressionWrapping: FunctionSignatureRule.FunctionBodyExpressionWrapping
) {
val code =
Expand Down Expand Up @@ -996,6 +1032,19 @@ class FunctionSignatureRuleTest {
).isFormattedAs(formattedCode)
}

@Test
fun `Issue 1527 - Given a function signature with an expression body which does not fit on the same line as the signature then do not reformat`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String) =
"some-result"
""".trimIndent()
functionSignatureWrappingRuleAssertThat(code)
.setMaxLineLength()
.hasNoLintViolations()
}

private companion object {
const val EOL_CHAR = '#'
const val UNEXPECTED_SPACES = " "
Expand Down