From d84711c3864771798b4e26360f609e7b11bc0018 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sun, 15 Oct 2023 20:22:12 +0200 Subject: [PATCH] De-indent the closing angle bracket of the type argument list and type parameter lists in ktlint_official code style (#2302) This makes indentation of parenthesis, brackets and angle brackets consistent. E.g. in multiline format the elements of the list (excluding the closing token) are indented one indent level deep. The closing token is kept at the same indent level as the opening token. Closes #2299 --- .../ruleset/standard/rules/IndentationRule.kt | 14 ++- .../standard/rules/AnnotationRuleTest.kt | 2 +- .../standard/rules/IndentationRuleTest.kt | 115 ++++++++++++++++-- .../rules/ParameterListWrappingRuleTest.kt | 4 +- .../SpacingAroundAngleBracketsRuleTest.kt | 26 ++-- .../rules/TrailingCommaOnCallSiteRuleTest.kt | 14 +-- .../TrailingCommaOnDeclarationSiteRuleTest.kt | 16 +-- 7 files changed, 150 insertions(+), 41 deletions(-) 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 375f44e75b..300d60d495 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 @@ -265,9 +265,19 @@ public class IndentationRule : startIndentContext(node) } + node.elementType == TYPE_ARGUMENT_LIST || + node.elementType == TYPE_PARAMETER_LIST -> + if (codeStyle == ktlint_official) { + // Contrary to the IntelliJ IDEA default formatter, do not indent the closing angle bracket + startIndentContext( + fromAstNode = node, + lastChildIndent = "", + ) + } else { + startIndentContext(node) + } + node.elementType == BINARY_WITH_TYPE || - node.elementType == TYPE_ARGUMENT_LIST || - node.elementType == TYPE_PARAMETER_LIST || node.elementType == USER_TYPE -> startIndentContext(node) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt index ae3d1509bd..ab4813140f 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt @@ -739,7 +739,7 @@ class AnnotationRuleTest { @Bar("bar") @Foo String - > = FooBar() + > = FooBar() """.trimIndent() @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") annotationRuleAssertThat(code) 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 94efec9dc5..42a81f7450 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 @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.ValueSource @Suppress("RemoveCurlyBracesFromTemplate") @@ -974,7 +975,65 @@ internal class IndentationRuleTest { """.trimIndent() @Test - fun `Given a class declaration implementing a super type with generics`() { + fun `Given ktlint-official code style and a class declaration implementing a super type with generics`() { + val formattedCode = + """ + open class Foo + class Bar + class FooBar : + Foo< + String, + Int + >, + Bar() { + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(4, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(5, 1, "Unexpected indentation (0) (should be 8)"), + LintViolation(6, 1, "Unexpected indentation (0) (should be 8)"), + LintViolation(7, 1, "Unexpected indentation (0) (should be 4)"), + LintViolation(8, 1, "Unexpected indentation (0) (should be 4)"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Given ktlint-official code style and a class declaration implementing a super type with generics (tab indentation)`() { + val formattedCode = + """ + open class Foo + class Bar + class FooBar : + ${TAB}Foo< + ${TAB}${TAB}String, + ${TAB}${TAB}Int + ${TAB}>, + ${TAB}Bar() { + } + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(INDENT_STYLE_TAB) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .hasLintViolations( + LintViolation(4, 1, "Unexpected indentation (0) (should be 1)"), + LintViolation(5, 1, "Unexpected indentation (0) (should be 2)"), + LintViolation(6, 1, "Unexpected indentation (0) (should be 2)"), + LintViolation(7, 1, "Unexpected indentation (0) (should be 1)"), + LintViolation(8, 1, "Unexpected indentation (0) (should be 1)"), + ).isFormattedAs(formattedCode) + } + + @ParameterizedTest(name = "CodeStyle: {0}") + @EnumSource( + value = CodeStyleValue::class, + mode = EnumSource.Mode.EXCLUDE, + names = ["ktlint_official"], + ) + fun `Given non-ktlint-official code style and a class declaration implementing a super type with generics`( + codeStyleValue: CodeStyleValue, + ) { val formattedCode = """ open class Foo @@ -988,6 +1047,7 @@ internal class IndentationRuleTest { } """.trimIndent() indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to codeStyleValue) .hasLintViolations( LintViolation(4, 1, "Unexpected indentation (0) (should be 4)"), LintViolation(5, 1, "Unexpected indentation (0) (should be 8)"), @@ -997,8 +1057,15 @@ internal class IndentationRuleTest { ).isFormattedAs(formattedCode) } - @Test - fun `Given a class declaration implementing a super type with generics (tab indentation)`() { + @ParameterizedTest(name = "CodeStyle: {0}") + @EnumSource( + value = CodeStyleValue::class, + mode = EnumSource.Mode.EXCLUDE, + names = ["ktlint_official"], + ) + fun `Given non-ktlint-official code style and a class declaration implementing a super type with generics (tab indentation)`( + codeStyleValue: CodeStyleValue, + ) { val formattedCode = """ open class Foo @@ -1013,6 +1080,7 @@ internal class IndentationRuleTest { """.trimIndent() indentationRuleAssertThat(code) .withEditorConfigOverride(INDENT_STYLE_TAB) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to codeStyleValue) .hasLintViolations( LintViolation(4, 1, "Unexpected indentation (0) (should be 1)"), LintViolation(5, 1, "Unexpected indentation (0) (should be 2)"), @@ -4507,7 +4575,39 @@ internal class IndentationRuleTest { } @Test - fun `Given a nested type parameter list`() { + fun `Given ktlint-official code style and a nested type parameter list`() { + val code = + """ + public class Foo< + Bar1 : String, + Bar2 : Map< + Int, + List + > + > {} + """.trimIndent() + val formattedCode = + """ + public class Foo< + Bar1 : String, + Bar2 : Map< + Int, + List + > + > {} + """.trimIndent() + indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to ktlint_official) + .isFormattedAs(formattedCode) + } + + @ParameterizedTest(name = "CodeStyle: {0}") + @EnumSource( + value = CodeStyleValue::class, + mode = EnumSource.Mode.EXCLUDE, + names = ["ktlint_official"], + ) + fun `Given non-ktlint-official code style and a nested type parameter list`(codeStyleValue: CodeStyleValue) { val code = """ public class Foo< @@ -4529,6 +4629,7 @@ internal class IndentationRuleTest { > {} """.trimIndent() indentationRuleAssertThat(code) + .withEditorConfigOverride(CODE_STYLE_PROPERTY to codeStyleValue) .isFormattedAs(formattedCode) } } @@ -4844,15 +4945,13 @@ internal class IndentationRuleTest { ) fun fooBar() """.trimIndent() - // Actually, the closing ">" should be de-indented in same way as is done with ")", "]" and "}". It is - // however indented to keep it in sync with other TYPE_ARGUMENT_LISTs which are formatted in this way. val formattedCode = """ context( FooBar< Foo, Bar - > + > ) fun fooBar() """.trimIndent() @@ -4861,7 +4960,7 @@ internal class IndentationRuleTest { LintViolation(2, 1, "Unexpected indentation (0) (should be 4)"), LintViolation(3, 1, "Unexpected indentation (0) (should be 8)"), LintViolation(4, 1, "Unexpected indentation (0) (should be 8)"), - LintViolation(5, 1, "Unexpected indentation (0) (should be 8)"), + LintViolation(5, 1, "Unexpected indentation (0) (should be 4)"), ).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 94aac3ea93..5bdb043133 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 @@ -498,7 +498,7 @@ class ParameterListWrappingRuleTest { A : GenericTypeWithALongLongALong1, B : GenericTypeWithALongLongALong2, C : GenericTypeWithALongLongALong3 - > constructor( + > constructor( parameterWithLongLongLongLongLongLongLongLongNameA: A, parameterWithLongLongLongLongLongLongLongLongNameB: B, parameterWithLongLongLongLongLongLongLongLongNameC: C @@ -530,7 +530,7 @@ class ParameterListWrappingRuleTest { public val fooBar: List< Foo, Bar - >, + >, ) """.trimIndent() parameterListWrappingRuleAssertThat(code).hasNoLintViolations() diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt index 6971019fe1..84aa1c3e3c 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundAngleBracketsRuleTest.kt @@ -16,11 +16,11 @@ class SpacingAroundAngleBracketsRuleTest { val c: Map = mapOf() val d: Map< Int, String - > = mapOf() + > = mapOf() val e: Map< Int, String - > = mapOf() + > = mapOf() val f: Map < Int, @@ -31,8 +31,8 @@ class SpacingAroundAngleBracketsRuleTest { Int, List< String - > - > = mapOf() + > + > = mapOf() """.trimIndent() val formattedCode = """ @@ -41,22 +41,22 @@ class SpacingAroundAngleBracketsRuleTest { val c: Map = mapOf() val d: Map< Int, String - > = mapOf() + > = mapOf() val e: Map< Int, String - > = mapOf() + > = mapOf() val f: Map< Int, String - > = mapOf() + > = mapOf() val g: Map> = mapOf() val h: Map< Int, List< String - > - > = mapOf() + > + > = mapOf() """.trimIndent() spacingAroundAngleBracketsRuleAssertThat(code) .addAdditionalRuleProvider { IndentationRule() } @@ -89,8 +89,8 @@ class SpacingAroundAngleBracketsRuleTest { Bar2 : Map< Int, List< String > - > - > {} + > + > {} """.trimIndent() val formattedCode = """ @@ -105,8 +105,8 @@ class SpacingAroundAngleBracketsRuleTest { Bar2 : Map< Int, List - > - > {} + > + > {} """.trimIndent() spacingAroundAngleBracketsRuleAssertThat(code) .addAdditionalRuleProvider { IndentationRule() } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRuleTest.kt index da13fd95cb..ab974300b3 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRuleTest.kt @@ -139,7 +139,7 @@ class TrailingCommaOnCallSiteRuleTest { val list1: List = emptyList() val list2: List< String, // The comma before the comment should be removed without removing the comment itself - > = emptyList() + > = emptyList() val list3: List< String, /* The comma before the comment should be removed without removing the comment itself */ > = emptyList() @@ -149,10 +149,10 @@ class TrailingCommaOnCallSiteRuleTest { val list1: List = emptyList() val list2: List< String // The comma before the comment should be removed without removing the comment itself - > = emptyList() + > = emptyList() val list3: List< String /* The comma before the comment should be removed without removing the comment itself */ - > = emptyList() + > = emptyList() """.trimIndent() trailingCommaOnCallSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_CALL_SITE_PROPERTY to false) @@ -170,20 +170,20 @@ class TrailingCommaOnCallSiteRuleTest { val list1: List = emptyList() val list2: List< String // The comma should be inserted before the comment - > = emptyList() + > = emptyList() val list3: List< String /* The comma should be inserted before the comment */ - > = emptyList() + > = emptyList() """.trimIndent() val formattedCode = """ val list1: List = emptyList() val list2: List< String, // The comma should be inserted before the comment - > = emptyList() + > = emptyList() val list3: List< String, /* The comma should be inserted before the comment */ - > = emptyList() + > = emptyList() """.trimIndent() trailingCommaOnCallSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_CALL_SITE_PROPERTY to true) 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 93aee4bdec..5998046ed7 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 @@ -146,11 +146,11 @@ class TrailingCommaOnDeclarationSiteRuleTest { class Foo2< A, B, // The comma before the comment should be removed without removing the comment itself - > {} + > {} class Foo3< A, B, /* The comma before the comment should be removed without removing the comment itself */ - > {} + > {} """.trimIndent() val formattedCode = """ @@ -158,11 +158,11 @@ class TrailingCommaOnDeclarationSiteRuleTest { class Foo2< A, B // The comma before the comment should be removed without removing the comment itself - > {} + > {} class Foo3< A, B /* The comma before the comment should be removed without removing the comment itself */ - > {} + > {} """.trimIndent() trailingCommaOnDeclarationSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_DECLARATION_SITE_PROPERTY to false) @@ -181,11 +181,11 @@ class TrailingCommaOnDeclarationSiteRuleTest { class Foo2< A, B // The comma should be inserted before the comment - > {} + > {} class Foo3< A, B /* The comma should be inserted before the comment */ - > {} + > {} """.trimIndent() val formattedCode = """ @@ -193,11 +193,11 @@ class TrailingCommaOnDeclarationSiteRuleTest { class Foo2< A, B, // The comma should be inserted before the comment - > {} + > {} class Foo3< A, B, /* The comma should be inserted before the comment */ - > {} + > {} """.trimIndent() trailingCommaOnDeclarationSiteRuleAssertThat(code) .withEditorConfigOverride(TRAILING_COMMA_ON_DECLARATION_SITE_PROPERTY to true)