diff --git a/CHANGELOG.md b/CHANGELOG.md index 447660124e..4e69a2969e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +* Do not indent class body for classes having a long super type list in code style `ktlint_official` as it is inconsistent compared to other class bodies `indent` [#2115](https://github.com/pinterest/ktlint/issues/2115) + ### Changed * Update dependency gradle to v8.2 ([#2105](https://github.com/pinterest/ktlint/pull/2105)) 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 39ae35af94..71e7a66898 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 @@ -19,6 +19,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLOSING_QUOTE import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONDITION import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_DELEGATION_CALL +import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.DELEGATED_SUPER_TYPE_ENTRY import com.pinterest.ktlint.rule.engine.core.api.ElementType.DESTRUCTURING_DECLARATION @@ -605,12 +606,17 @@ public class IndentationRule : } val primaryConstructor = node.findChildByType(PRIMARY_CONSTRUCTOR) - if (codeStyle == ktlint_official && primaryConstructor != null) { + val containsConstructorKeyword = primaryConstructor?.findChildByType(CONSTRUCTOR_KEYWORD) != null + if (codeStyle == ktlint_official && primaryConstructor != null && containsConstructorKeyword) { // Indent both constructor and super type list nextToAstNode = startIndentContext( fromAstNode = primaryConstructor.getPrecedingLeadingCommentsAndWhitespaces(), - toAstNode = nextToAstNode, + toAstNode = + node + .findChildByType(SUPER_TYPE_LIST) + ?.lastChildLeafOrSelf() + ?: nextToAstNode, ).prevCodeLeaf() } else { node 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 fe5372a8d5..64966e8204 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 @@ -4958,22 +4958,13 @@ internal class IndentationRuleTest { indentationRuleAssertThat(code).hasNoLintViolations() } - @Test - fun `Issue 1916 - Given a class declaration with an annotation before the constructor`() { - val code = - """ - class Foo - @Bar1 @Bar2 - constructor( - foo1: Foo1, - foo2: Foo2, - ) { - fun foo() = "foo" - } - """.trimIndent() - val formattedCode = - """ - class Foo + @Nested + inner class `Issue 1916, issue 2115 - Given the ktlint_official code style and a class declaration with an annotated constructor` { + @Test + fun `Issue 1916, issue 2115 - Given a class declaration with an annotation before the constructor keyword`() { + val code = + """ + class Foo @Bar1 @Bar2 constructor( foo1: Foo1, @@ -4981,39 +4972,36 @@ internal class IndentationRuleTest { ) { fun foo() = "foo" } - """.trimIndent() - indentationRuleAssertThat(code) - .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) - .hasLintViolations( - LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(3, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(4, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(5, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(6, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(7, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(8, 1, "Unexpected indentation (0) (should be 4)"), - ).isFormattedAs(formattedCode) - } + """.trimIndent() + val formattedCode = + """ + class Foo + @Bar1 @Bar2 + constructor( + foo1: Foo1, + foo2: Foo2, + ) { + fun foo() = "foo" + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(3, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(4, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(7, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(8, 1, "Unexpected indentation (0) (should be 4)"), + ).isFormattedAs(formattedCode) + } - @Test - fun `Issue 1916 - Given a class declaration with an annotation before the constructor nad having a super type list`() { - val code = - """ - class Foo - @Bar1 @Bar2 - constructor( - foo1: Foo1, - foo2: Foo2, - ) : Foobar( - "foobar1", - "foobar2", - ) { - fun foo() = "foo" - } - """.trimIndent() - val formattedCode = - """ - class Foo + @Test + fun `Issue 1916, issue 2115 - Given a class declaration with an annotation before the constructor and having a single super type`() { + val code = + """ + class Foo @Bar1 @Bar2 constructor( foo1: Foo1, @@ -5024,21 +5012,96 @@ internal class IndentationRuleTest { ) { fun foo() = "foo" } - """.trimIndent() - indentationRuleAssertThat(code) - .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) - .hasLintViolations( - LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(3, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(4, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(5, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(6, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(7, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(8, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(9, 1, "Unexpected indentation (0) (should be 4)"), - LintViolation(10, 1, "Unexpected indentation (4) (should be 8)"), - LintViolation(11, 1, "Unexpected indentation (0) (should be 4)"), - ).isFormattedAs(formattedCode) + """.trimIndent() + val formattedCode = + """ + class Foo + @Bar1 @Bar2 + constructor( + foo1: Foo1, + foo2: Foo2, + ) : Foobar( + "foobar1", + "foobar2", + ) { + fun foo() = "foo" + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(3, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(4, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(7, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(8, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(9, 1, "Unexpected indentation (0) (should be 4)"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Issue 1916, issue 2115 - Given a class declaration with an annotation before the constructor and having multiple super types`() { + val code = + """ + class Foo + @Bar1 @Bar2 + constructor( + foo1: Foo1, + foo2: Foo2, + ) : Foobar1( + "foobar1", + "foobar2", + ), FooBar2 { + fun foo() = "foo" + } + """.trimIndent() + val formattedCode = + """ + class Foo + @Bar1 @Bar2 + constructor( + foo1: Foo1, + foo2: Foo2, + ) : Foobar1( + "foobar1", + "foobar2", + ), FooBar2 { + fun foo() = "foo" + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(3, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(4, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(7, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(8, 1, "Unexpected indentation (4) (should be 8)"), + LintViolation(9, 1, "Unexpected indentation (0) (should be 4)"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Issue 2115 - Given a class without an explicit constructor and with a long super type list then do not indent the class body`() { + val code = + """ + class Foo( + val bar1: Bar, + val bar2: Bar, + ) : FooBar(bar1, bar2), + BarFoo1, + BarFoo2 { + // body + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasNoLintViolations() + } } @Test