Skip to content

Commit

Permalink
Do not wrap chained method calls containing a single call expression
Browse files Browse the repository at this point in the history
Closes #2797
  • Loading branch information
paul-dingemans committed Sep 25, 2024
1 parent 70c16fb commit 9a10ec0
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
8 changes: 7 additions & 1 deletion documentation/snapshot/docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ In a multiline method chain, the chain operators (`.` or `?.`) have to be aligne

Multiple chained methods on a single line are allowed as long as the maximum line length, and the maximum number of chain operators are not exceeded. Under certain conditions, it is allowed that the expression before the first and/or the expression after the last chain operator is a multiline expression.

The `.` in `java.class` is ignored when wrapping on chain operators.
The `.` in `java.class` is ignored when wrapping on chain operators. The package identifiers in a method chain containing only one call expression (e.g. a constructor or method call) are being ignored as long as the entire chain fits on a single line.

!!! warning
A binary expression for which the left and/or right operand consist of a method chain are currently being ignored by this rule. Please reach out, if you can help to determine what the best strategy is to deal with such kind of expressions.
Expand Down Expand Up @@ -269,6 +269,9 @@ The `.` in `java.class` is ignored when wrapping on chain operators.
"""
Some text
""".trimIndent().foo().bar()
// Assume that the last allowed character is
// at the X character on the right X
val foo5 = foo.bar.baz.foo.bar.baz.FooBarBaz()
```

=== "[:material-heart-off-outline:](#) Disallowed"
Expand Down Expand Up @@ -305,6 +308,9 @@ The `.` in `java.class` is ignored when wrapping on chain operators.
Some text
""".trimIndent().foo()
.bar()
// Assume that the last allowed character is
// at the X character on the right X
val foo5 = foo.bar.baz.foo.bar.baz.FooBarBaz()
```

| Configuration setting | ktlint_official | intellij_idea | android_studio |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ public class ChainMethodContinuationRule :
false
}

chainOperators.count { it.nextSibling()?.elementType == CALL_EXPRESSION } <= 1 -> {
// Allow:
// foo.bar.bar.foo.bar.baz.Foo()
false
}

!hasNewlineBeforeFirstChainOperator && !hasNewlineAfterLastChainOperator -> {
// Allow:
// listOf(1, 2, 3).filter { it > 2 }.filter { it > 3 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1132,4 +1132,54 @@ class ChainMethodContinuationRuleTest {
.withEditorConfigOverride(FORCE_MULTILINE_WHEN_CHAIN_OPERATOR_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY to "unset")
.hasNoLintViolations()
}

@Test
fun `Issue 2797 - Given a chained method call then only wrap when it contains multiple call expressions`() {
val code =
"""
val foo1 = foo.bar.baz.FooBarBaz()
val foo2 = foo.bar.baz.FooBarBaz().foo()
val foo3 = foo.bar.baz.FooBarBaz().foo.filter { it == "foo" }
""".trimIndent()
val formattedCode =
"""
val foo1 = foo.bar.baz.FooBarBaz()
val foo2 = foo.bar.baz
.FooBarBaz()
.foo()
val foo3 = foo.bar.baz
.FooBarBaz()
.foo
.filter { it == "foo" }
""".trimIndent()
chainMethodContinuationRuleAssertThat(code)
.hasLintViolations(
LintViolation(2, 23, "Expected newline before '.'"),
LintViolation(2, 35, "Expected newline before '.'"),
LintViolation(3, 23, "Expected newline before '.'"),
LintViolation(3, 35, "Expected newline before '.'"),
LintViolation(3, 39, "Expected newline before '.'"),
).isFormattedAs(formattedCode)
}

@Test
fun `Issue 2797 - Given a chained method call with a single call expressions exceeding max line length then wrap`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
val foo1 = foo.bar.baz.FooBarBaz()
val foo2 = foo.bar.baz.FooBarBazz()
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
val foo1 = foo.bar.baz.FooBarBaz()
val foo2 = foo.bar.baz
.FooBarBazz()
""".trimIndent()
chainMethodContinuationRuleAssertThat(code)
.setMaxLineLength()
.hasLintViolation(3, 23, "Expected newline before '.'")
.isFormattedAs(formattedCode)
}
}

0 comments on commit 9a10ec0

Please sign in to comment.