diff --git a/documentation/snapshot/docs/rules/standard.md b/documentation/snapshot/docs/rules/standard.md index 33879cd566..17992b207e 100644 --- a/documentation/snapshot/docs/rules/standard.md +++ b/documentation/snapshot/docs/rules/standard.md @@ -758,7 +758,7 @@ Enforces the parameters of a function literal and the arrow to be written on the If the function literal contains multiple parameters and at least one parameter other than the first parameter starts on a new line than all parameters and the arrow are wrapped to separate lines. -=== "[:material-heart:](#) Ktlint" +=== "[:material-heart:](#) Ktlint (ktlint_official)" ```kotlin val foobar1 = { foo + bar } @@ -775,6 +775,27 @@ If the function literal contains multiple parameters and at least one parameter foo + bar } val foobar5 = { foo: Foo, bar: Bar -> foo + bar } + val foobar6 = + { + foo: Foo, + bar: Bar, + -> + foo + bar + } + + // Assume that the last allowed character is + // at the X character on the right X + val foobar7 = + barrrrrrrrrrrrrr { + fooooooooooooooo: Foo + -> + foo.repeat(2) + } + ``` + +=== "[:material-heart:](#) Ktlint (non ktlint_official)" + + ```kotlin val foobar6 = { foo: Foo, @@ -787,7 +808,7 @@ If the function literal contains multiple parameters and at least one parameter // at the X character on the right X val foobar7 = barrrrrrrrrrrrrr { - fooooooooooooooo: Foo + fooooooooooooooo: Foo -> foo.repeat(2) } diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt index e4204671e8..0db529371c 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/RuleExecutionContext.kt @@ -196,9 +196,9 @@ internal class RuleExecutionContext private constructor( @Deprecated(message = "Remove in Ktlint 2.0") private fun ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision).onlyEmit() = { - offset: Int, - errorMessage: String, - canBeAutoCorrected: Boolean, + offset: Int, + errorMessage: String, + canBeAutoCorrected: Boolean, -> this(offset, errorMessage, canBeAutoCorrected) Unit diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index b50be93c1a..73c6bed24d 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -517,14 +517,20 @@ public class IndentationRule : private fun ASTNode.calculateIndentOfFunctionLiteralParameters() = if (isFirstParameterOfFunctionLiteralPrecededByNewLine()) { - // val fieldExample = - // LongNameClass { - // paramA, - // paramB, - // paramC -> - // ClassB(paramA, paramB, paramC) - // } - indentConfig.indent.repeat(2) + if (codeStyle == ktlint_official) { + // Indent with single indent as defined in Kotlin Coding conventions + indentConfig.indent + } else { + // Comply with default IDEA formatting although it is not compliant with Kotlin Coding conventions + // val fieldExample = + // LongNameClass { + // paramA, + // paramB, + // paramC -> + // ClassB(paramA, paramB, paramC) + // } + indentConfig.indent.repeat(2) + } } else { // Allow default IntelliJ IDEA formatting: // val fieldExample = diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt index 07abbb57f9..e98c67aecd 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt @@ -2603,35 +2603,75 @@ internal class IndentationRuleTest { indentationRuleAssertThat(code).hasNoLintViolations() } - @Test - fun `Given an incorrectly indented lambda block`() { - val code = - """ - fun main() { - foo.func { - param1, param2 -> + @Nested + inner class `Given an incorrectly indented lambda block` { + @Test + fun `Issue 2816 - Given ktlint_official code style`() { + val code = + """ + fun main() { + foo.func { + param1, param2 -> + doSomething() + doSomething2() + } + } + """.trimIndent() + val formattedCode = + """ + fun main() { + foo.func { + param1, param2 -> doSomething() doSomething2() } - } - """.trimIndent() - val formattedCode = - """ - fun main() { - foo.func { + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(4, 1, "Unexpected indentation (12) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (12) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (8) (should be 4)"), + ).isFormattedAs(formattedCode) + } + + @ParameterizedTest(name = "Code style: {0}") + @EnumSource( + value = CodeStyleValue::class, + mode = EnumSource.Mode.EXCLUDE, + names = ["ktlint_official"], + ) + fun `Given code style other than 'ktlint_official'`(codeStyleValue: CodeStyleValue) { + val code = + """ + fun main() { + foo.func { param1, param2 -> - doSomething() - doSomething2() + doSomething() + doSomething2() + } } - } - """.trimIndent() - indentationRuleAssertThat(code) - .hasLintViolations( - LintViolation(3, 1, "Unexpected indentation (8) (should be 12)"), - LintViolation(4, 1, "Unexpected indentation (12) (should be 8)"), - LintViolation(5, 1, "Unexpected indentation (12) (should be 8)"), - LintViolation(6, 1, "Unexpected indentation (8) (should be 4)"), - ).isFormattedAs(formattedCode) + """.trimIndent() + val formattedCode = + """ + fun main() { + foo.func { + param1, param2 -> + doSomething() + doSomething2() + } + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to codeStyleValue) + .hasLintViolations( + LintViolation(3, 1, "Unexpected indentation (8) (should be 12)"), + LintViolation(4, 1, "Unexpected indentation (12) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (12) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (8) (should be 4)"), + ).isFormattedAs(formattedCode) + } } @Test @@ -3964,27 +4004,27 @@ internal class IndentationRuleTest { val code = """ val foo1: (String) -> String = { // Some comment which should not be moved to the next line when formatting - s: String + s: String -> // does something with string } val foo2: (String) -> String = { - // Some comment which has to be indented with the parameter list - s: String + // Some comment which has to be indented with the parameter list + s: String -> // does something with string } val foo3 = { // Some comment which should not be moved to the next line when formatting - s: String, + s: String, -> // does something with string } val foo4 = { - // Some comment which has to be indented with the parameter list - s: String, + // Some comment which has to be indented with the parameter list + s: String, -> // does something with string } @@ -3992,39 +4032,39 @@ internal class IndentationRuleTest { val formattedCode = """ val foo1: (String) -> String = { // Some comment which should not be moved to the next line when formatting - s: String + s: String -> // does something with string } val foo2: (String) -> String = { - // Some comment which has to be indented with the parameter list - s: String + // Some comment which has to be indented with the parameter list + s: String -> // does something with string } val foo3 = { // Some comment which should not be moved to the next line when formatting - s: String, + s: String, -> // does something with string } val foo4 = { - // Some comment which has to be indented with the parameter list - s: String, + // Some comment which has to be indented with the parameter list + s: String, -> // does something with string } """.trimIndent() indentationRuleAssertThat(code) .hasLintViolations( - LintViolation(2, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(8, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(9, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(15, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(21, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(22, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(2, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(8, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(9, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(15, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(21, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(22, 1, "Unexpected indentation (8) (should be 4)"), ).isFormattedAs(formattedCode) } @@ -4033,14 +4073,14 @@ internal class IndentationRuleTest { val code = """ val foo1: (String) -> String = { - s: String + s: String -> // does something with string } val foo2 = { - // Trailing comma on last element is allowed and does not have effect - s: String, + // Trailing comma on last element is allowed and does not have effect + s: String, -> // does something with string } @@ -4048,23 +4088,23 @@ internal class IndentationRuleTest { val formattedCode = """ val foo1: (String) -> String = { - s: String + s: String -> // does something with string } val foo2 = { - // Trailing comma on last element is allowed and does not have effect - s: String, + // Trailing comma on last element is allowed and does not have effect + s: String, -> // does something with string } """.trimIndent() indentationRuleAssertThat(code) .hasLintViolations( - LintViolation(2, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(8, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(9, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(2, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(8, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(9, 1, "Unexpected indentation (8) (should be 4)"), ).isFormattedAs(formattedCode) } @@ -4073,16 +4113,16 @@ internal class IndentationRuleTest { val code = """ val foo1: (String, String) -> String = { - s1: String, - s2: String + s1: String, + s2: String -> // does something with strings } val foo2 = { - s1: String, - // Trailing comma on last element is allowed and does not have effect - s2: String, + s1: String, + // Trailing comma on last element is allowed and does not have effect + s2: String, -> // does something with strings } @@ -4090,27 +4130,27 @@ internal class IndentationRuleTest { val formattedCode = """ val foo1: (String, String) -> String = { - s1: String, - s2: String + s1: String, + s2: String -> // does something with strings } val foo2 = { - s1: String, - // Trailing comma on last element is allowed and does not have effect - s2: String, + s1: String, + // Trailing comma on last element is allowed and does not have effect + s2: String, -> // does something with strings } """.trimIndent() indentationRuleAssertThat(code) .hasLintViolations( - LintViolation(2, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(3, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(9, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(10, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(11, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(2, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(3, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(9, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(10, 1, "Unexpected indentation (8) (should be 4)"), + LintViolation(11, 1, "Unexpected indentation (8) (should be 4)"), ).isFormattedAs(formattedCode) } @@ -5002,9 +5042,9 @@ internal class IndentationRuleTest { """ val bar = BarBarBarBar { - paramA, - paramB, - paramC -> + paramA, + paramB, + paramC -> Bar(paramA, paramB, paramC) } """.trimIndent() @@ -5012,10 +5052,6 @@ internal class IndentationRuleTest { .addAdditionalRuleProvider { ParameterListWrappingRule() } .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) .hasLintViolationForAdditionalRule(2, 20, "Parameter should start on a newline") -// .hasLintViolations( -// LintViolation(3, 1, "Unexpected indentation (19) (should be 12)"), -// LintViolation(4, 1, "Unexpected indentation (19) (should be 12)"), -// ) .isFormattedAs(formattedCode) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRuleTest.kt index b332d67962..09e08b3eff 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRuleTest.kt @@ -197,9 +197,9 @@ class ParameterListWrappingRuleTest { """ val fieldExample = LongNameClass { - paramA, - paramB, - paramC -> + paramA, + paramB, + paramC -> ClassB(paramA, paramB, paramC) } """.trimIndent() diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRuleTest.kt index 538f3cbed5..0c520fb513 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRuleTest.kt @@ -449,14 +449,14 @@ class TrailingCommaOnDeclarationSiteRuleTest { """ val fooBar1: (Int, Int) -> Int = { foo, bar, -> foo * bar } val fooBar2: (Int, Int) -> Int = { - foo, - bar, // The comma before the comment should be removed without removing the comment itself + foo, + bar, // The comma before the comment should be removed without removing the comment itself -> foo * bar } val fooBar3: (Int, Int) -> Int = { - foo, - bar, /* The comma before the comment should be removed without removing the comment itself */ + foo, + bar, /* The comma before the comment should be removed without removing the comment itself */ -> foo * bar } @@ -465,14 +465,14 @@ class TrailingCommaOnDeclarationSiteRuleTest { """ val fooBar1: (Int, Int) -> Int = { foo, bar -> foo * bar } val fooBar2: (Int, Int) -> Int = { - foo, - bar // The comma before the comment should be removed without removing the comment itself + foo, + bar // The comma before the comment should be removed without removing the comment itself -> foo * bar } val fooBar3: (Int, Int) -> Int = { - foo, - bar /* The comma before the comment should be removed without removing the comment itself */ + foo, + bar /* The comma before the comment should be removed without removing the comment itself */ -> foo * bar } @@ -481,8 +481,8 @@ class TrailingCommaOnDeclarationSiteRuleTest { .withEditorConfigOverride(TRAILING_COMMA_ON_DECLARATION_SITE_PROPERTY to false) .hasLintViolations( LintViolation(1, 44, "Unnecessary trailing comma before \"->\""), - LintViolation(4, 12, "Unnecessary trailing comma before \"->\""), - LintViolation(10, 12, "Unnecessary trailing comma before \"->\""), + LintViolation(4, 8, "Unnecessary trailing comma before \"->\""), + LintViolation(10, 8, "Unnecessary trailing comma before \"->\""), ).isFormattedAs(formattedCode) } @@ -492,14 +492,14 @@ class TrailingCommaOnDeclarationSiteRuleTest { """ val fooBar1: (Int, Int) -> Int = { foo, bar -> foo * bar } val fooBar2: (Int, Int) -> Int = { - foo, - bar // The comma should be inserted before the comment + foo, + bar // The comma should be inserted before the comment -> foo * bar } val fooBar3: (Int, Int) -> Int = { - foo, - bar /* The comma should be inserted before the comment */ + foo, + bar /* The comma should be inserted before the comment */ -> foo * bar } @@ -508,14 +508,14 @@ class TrailingCommaOnDeclarationSiteRuleTest { """ val fooBar1: (Int, Int) -> Int = { foo, bar -> foo * bar } val fooBar2: (Int, Int) -> Int = { - foo, - bar, // The comma should be inserted before the comment + foo, + bar, // The comma should be inserted before the comment -> foo * bar } val fooBar3: (Int, Int) -> Int = { - foo, - bar, /* The comma should be inserted before the comment */ + foo, + bar, /* The comma should be inserted before the comment */ -> foo * bar } @@ -523,8 +523,8 @@ class TrailingCommaOnDeclarationSiteRuleTest { trailingCommaOnDeclarationSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_DECLARATION_SITE_PROPERTY to true) .hasLintViolations( - LintViolation(4, 12, "Missing trailing comma before \"->\""), - LintViolation(10, 12, "Missing trailing comma before \"->\""), + LintViolation(4, 8, "Missing trailing comma before \"->\""), + LintViolation(10, 8, "Missing trailing comma before \"->\""), ).isFormattedAs(formattedCode) } @@ -1047,23 +1047,23 @@ class TrailingCommaOnDeclarationSiteRuleTest { val code = """ val foo = { - string: String, - int: Int -> + string: String, + int: Int -> // do something } """.trimIndent() val formattedCode = """ val foo = { - string: String, - int: Int, + string: String, + int: Int, -> // do something } """.trimIndent() trailingCommaOnDeclarationSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_DECLARATION_SITE_PROPERTY to true) - .hasLintViolation(3, 17, "Missing trailing comma and newline before \"->\"") + .hasLintViolation(3, 13, "Missing trailing comma and newline before \"->\"") .isFormattedAs(formattedCode) } }