diff --git a/CHANGELOG.md b/CHANGELOG.md index 9266dec834..b1a0937a9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). * Ignore override of function in rule `function-naming` [#2271](https://github.com/pinterest/ktlint/issue/2271) * Do not replace function body having a return statement only in case the return statement contains an intermediate exit point 'function-expression-body' [#2269](https://github.com/pinterest/ktlint/issue/2269) * Prevent wrapping of nested multiline binary expression before operation reference as it results in a compilation error `multiline-expression-wrapping` [#2286](https://github.com/pinterest/ktlint/issue/2286) +* Force blank line before object declaration if preceded by another declaration `blank-line-before-declaration` [#2284](https://github.com/pinterest/ktlint/issues/2284) ### Changed diff --git a/documentation/snapshot/docs/rules/experimental.md b/documentation/snapshot/docs/rules/experimental.md index 6dc3237dc5..6cd18756fe 100644 --- a/documentation/snapshot/docs/rules/experimental.md +++ b/documentation/snapshot/docs/rules/experimental.md @@ -19,8 +19,11 @@ Wraps binary expression at the operator reference whenever the binary expression // Assume that the last allowed character is // at the X character on the right X if ((leftHandSideExpression && rightHandSideExpression) || - (leftHandSideLongExpression && - rightHandSideLongExpression)) { + ( + leftHandSideLongExpression && + rightHandSideExpression + ) + ) { // do something } } @@ -32,7 +35,9 @@ Wraps binary expression at the operator reference whenever the binary expression fun foo() { // Assume that the last allowed character is // at the X character on the right X - if ((leftHandSideExpression && rightHandSideExpression) || (leftHandSideLongExpression && rightHandSideLongExpression)) { + if ((leftHandSideExpression && rightHandSideExpression) || + (leftHandSideLongExpression && rightHandSideExpression) + ) { // do something } } @@ -74,9 +79,10 @@ This rule can be configured with `.editorconfig` property [`ktlint_chain_method_ }?.map { 2 * it } - val foo3 = foo().bar().map { - it.foobar() - } + val foo3 = + foo().bar().map { + it.foobar() + } val foo4 = """ Some text @@ -107,7 +113,8 @@ This rule can be configured with `.editorconfig` property [`ktlint_chain_method_ ?.map { 2 * it } - val foo3 = foo() + val foo3 = + foo() .bar().map { it.foobar() } @@ -133,23 +140,28 @@ The other code styles allow an infinite amount of parameters on the same line (a ```kotlin // Assume that max_line_length is not exceeded when written as single line class Foo0 + class Foo1( - a: Any + a: Any, ) + class Foo2( a: Any, - b: Any + b: Any, ) + class Foo3( @Foo a: Any, b: Any, - c: Any + c: Any, ) + class Foo4( a: Any, b: Any, - c: Any + c: Any, ) : FooBar(a, c) + class Foo5 : FooBar( "bar1", @@ -157,26 +169,29 @@ The other code styles allow an infinite amount of parameters on the same line (a ) { // body } + class Foo6( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, - bar2 + bar2, ) { // body } + class Foo7( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, - bar2 + bar2, ), BarFoo1, BarFoo2 { // body } + class Foo8 constructor( val bar1: Bar, @@ -184,8 +199,8 @@ The other code styles allow an infinite amount of parameters on the same line (a ) : FooBar(bar1, bar2), BarFoo1, BarFoo2 { - // body - } + // body + } ``` === "[:material-heart-off-outline:](#) Disallowed (ktlint_official)" @@ -193,36 +208,44 @@ The other code styles allow an infinite amount of parameters on the same line (a ```kotlin // Assume that max_line_length is not exceeded when written as single line class Foo0() + class Foo1(a: Any) + class Foo2(a: Any, b: Any) + class Foo3(@Foo a: Any, b: Any, c: Any) + class Foo4(a: Any, b: Any, c: Any) : FooBar(a, c) + class Foo5 : FooBar( "bar1", "bar2", ) { // body } + class Foo6( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, - bar2 + bar2, ) { // body } + class Foo7( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, - bar2 + bar2, ), BarFoo1, BarFoo2 { // body } + class Foo8 constructor( val bar1: Bar, @@ -240,18 +263,25 @@ The other code styles allow an infinite amount of parameters on the same line (a // Assume that the last allowed character is // at the X character on the right X class Foo0 + class Foo1( - a: Any + a: Any, ) + class Foo2(a: Any) + class Foo3( a: Any, - b: Any + b: Any, ) + class Foo4(a: Any, b: Any) + class Foo5(@Foo a: Any, b: Any, c: Any) + class Foo6(a: Any, b: Any, c: Any) : FooBar(a, c) + class Foo7 : FooBar( "bar1", "bar2", @@ -260,16 +290,17 @@ The other code styles allow an infinite amount of parameters on the same line (a } class Foo8( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, bar2 ) { // body } + class Foo9( val bar1: Bar, - val bar2: Bar + val bar2: Bar, ) : FooBar( bar1, bar2 @@ -278,6 +309,7 @@ The other code styles allow an infinite amount of parameters on the same line (a BarFoo2 { // body } + class Foo10 constructor( val bar1: Bar, @@ -295,6 +327,7 @@ The other code styles allow an infinite amount of parameters on the same line (a // Assume that the last allowed character is // at the X character on the right X class Foo0() + class Foo6(a: Any, b: Any, c: Any) : FooBar(a, c) ``` @@ -311,20 +344,27 @@ Rewrites a function body only containing a `return` or `throw` expression to an ```kotlin fun foo1() = "foo" + fun foo2(): String = "foo" + fun foo3(): Unit = throw IllegalArgumentException("some message") + fun foo4(): Foo = throw IllegalArgumentException("some message") + fun foo5() { return "foo" // some comment } + fun foo6(): String { /* some comment */ return "foo" } + fun foo7() { throw IllegalArgumentException("some message") /* some comment */ } + fun foo8(): Foo { throw IllegalArgumentException("some message") // some comment @@ -336,12 +376,15 @@ Rewrites a function body only containing a `return` or `throw` expression to an fun foo1() { return "foo" } + fun foo2(): String { return "foo" } + fun foo3() { throw IllegalArgumentException("some message") } + fun foo4(): Foo { throw IllegalArgumentException("some message") } @@ -358,33 +401,33 @@ If the function literal contains multiple parameter and at least one parameter o === "[:material-heart:](#) Ktlint" ```kotlin - val foobar1 = { foo + bar } - val foobar2 = - { - foo + bar - } - val foobar3 = - { foo: Foo -> - foo.repeat(2) - } - val foobar4 = - { foo: Foo, bar: Bar -> - foo + bar - } - val foobar5 = { foo: Foo, bar: Bar -> foo + bar } - val foobar6 = - { - foo: Foo, - bar: Bar - -> - foo + bar - } - + val foobar1 = { foo + bar } + val foobar2 = + { + foo + bar + } + val foobar3 = + { foo: Foo -> + foo.repeat(2) + } + val foobar4 = + { foo: Foo, bar: Bar -> + 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 + barrrrrrrrrrrrrr { + fooooooooooooooo: Foo -> foo.repeat(2) } @@ -424,6 +467,7 @@ Enforce a single whitespace between the modifier list and the function type. ```kotlin val foo: suspend () -> Unit = {} + suspend fun bar(baz: suspend () -> Unit) = baz() ``` @@ -431,6 +475,7 @@ Enforce a single whitespace between the modifier list and the function type. ```kotlin val foo: suspend() -> Unit = {} + suspend fun bar(baz: suspend () -> Unit) = baz() ``` diff --git a/documentation/snapshot/docs/rules/standard.md b/documentation/snapshot/docs/rules/standard.md index 4018bbea3d..243b91d2ce 100644 --- a/documentation/snapshot/docs/rules/standard.md +++ b/documentation/snapshot/docs/rules/standard.md @@ -8,25 +8,30 @@ Multiple annotations should be on a separate line than the annotated declaration // A single annotation (without parameters) is allowed on same line as annotated construct @FunctionalInterface class FooBar { @JvmField var foo: String + @Test fun bar() {} } + // A class or function parameter may have a single annotation with parameter(s) on the same line - class Foo(@Path("fooId") val fooId: String) - class Bar( - @NotNull("fooId") val fooId: String, - @NotNull("bar") bar: String + class Foo( + @Path("fooId") val fooId: String, + @NotNull("bar") bar: String, ) + // Multiple annotations (without parameters) are allowed on the same line @Foo @Bar class FooBar { @Foo @Bar var foo: String + @Foo @Bar fun bar() {} } + // An array of annotations (without parameters) is allowed on same line as annotated construct @[Foo Bar] class FooBar2 { @[Foo Bar] var foo: String + @[Foo Bar] fun bar() {} } ``` @@ -54,23 +59,23 @@ Requires a blank line before any class or function declaration. No blank line is === "[:material-heart:](#) Ktlint" ```kotlin - const val foo1 = "foo1" - + const val FOO_1 = "foo1" + class FooBar { val foo2 = "foo2" val foo3 = "foo3" - + fun bar1() { - val foo4 = "foo4" - val foo5 = "foo5" + val foo4 = "foo4" + val foo5 = "foo5" } - + fun bar2() = "bar" - + val foo6 = "foo3" val foo7 = "foo4" - - enum class Foo {} + + enum class Foo } ``` @@ -78,16 +83,21 @@ Requires a blank line before any class or function declaration. No blank line is ```kotlin const val foo1 = "foo1" + class FooBar { val foo2 = "foo2" val foo3 = "foo3" + fun bar1() { val foo4 = "foo4" val foo5 = "foo5" } + fun bar2() = "bar" + val foo6 = "foo3" val foo7 = "foo4" + enum class Foo {} } ``` @@ -150,7 +160,7 @@ Enum entry names should be uppercase underscore-separated or upper camel-case se FOO, Foo, FOO_BAR, - FooBar + FooBar, } ``` === "[:material-heart-off-outline:](#) Disallowed" @@ -191,7 +201,7 @@ Rewrites the function signature to a single line when possible (e.g. when not ex fun foooooooo( a: Any, b: Any, - c: Any + c: Any, ): String { // body } @@ -322,7 +332,7 @@ Indentation formatting - respects `.editorconfig` `indent_size` with no continua foobar( a, b, - c + c, ) } ``` @@ -333,7 +343,7 @@ Indentation formatting - respects `.editorconfig` `indent_size` with no continua foobar( a, b, - c + c, ) } ``` @@ -353,6 +363,7 @@ Enforce naming of class and objects. ```kotlin class Foo + class Foo1 ``` === "[:material-heart:](#) Ktlint JUnit Test" @@ -390,6 +401,7 @@ Enforce naming of function. ```kotlin fun foo() {} + fun fooBar() {} ``` === "[:material-heart:](#) Ktlint Test" @@ -460,8 +472,7 @@ Enforce naming of property. var foo2: Foo = Foo() // By definition not immutable class Bar { - val foo1 = Foo() // In case developer want to communicate that Foo is mutable - val FOO1 = Foo() // In case developer want to communicate that Foo is deeply immutable + val foo1 = "foo1" // Class properties always start with lowercase, const is not allowed const val FOO_BAR = "FOO-BAR" // By definition deeply immutable @@ -471,6 +482,11 @@ Enforce naming of property. private val _elementList = mutableListOf() val elementList: List get() = _elementList + + companion object { + val foo1 = Foo() // In case developer want to communicate that Foo is mutable + val FOO1 = Foo() // In case developer want to communicate that Foo is deeply immutable + } } ``` === "[:material-heart-off-outline:](#) Disallowed" @@ -506,7 +522,7 @@ Disallow blank lines to be used in lists before the first element, between eleme === "[:material-heart:](#) Ktlint" ```kotlin - class FooBar: + class FooBar : Foo, Bar { // body @@ -516,7 +532,7 @@ Disallow blank lines to be used in lists before the first element, between eleme === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - class FooBar: + class FooBar : Foo, @@ -557,7 +573,7 @@ Disallow blank lines to be used in lists before the first element, between eleme ```kotlin class BiAdapter( val adapter1: A1, - val adapter2: A2 + val adapter2: A2, ) : RecyclerView.Adapter() where A1 : RecyclerView.Adapter, A1 : ComposableAdapter.ViewTypeProvider, A2 : RecyclerView.Adapter, A2 : ComposableAdapter.ViewTypeProvider { @@ -609,22 +625,24 @@ Disallow blank lines to be used in lists before the first element, between eleme === "[:material-heart:](#) Ktlint" ```kotlin - val foobar = foobar( - "foo", - "bar", - ) + val foobar = + foobar( + "foo", + "bar", + ) ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val foobar = foobar( - - "foo", - - "bar", - - ) + val foobar = + foobar( + + "foo", + + "bar", + + ) ``` *Value parameter list* @@ -822,10 +840,12 @@ Ensures that lines do not exceed the given length of `.editorconfig` property `m // line length is exceeded. package com.toooooooooooooooooooooooooooo.long import com.tooooooooooooooooooooooooooooo.long + val foo = """ fooooooooooooooooooooooooooooooooooooooooo """ + @Test fun `Test description which is toooooooooooo long`() { } @@ -852,7 +872,9 @@ Consistent order of modifiers ```kotlin abstract class A { protected open val v = "" + internal open suspend fun f(v: Any): Any = "" + protected lateinit var lv: String } ``` @@ -861,7 +883,9 @@ Consistent order of modifiers ```kotlin abstract class A { open protected val v = "" + open suspend internal fun f(v: Any): Any = "" + lateinit protected var lv: String } ``` @@ -904,6 +928,7 @@ No blank lines before `}`. fun main() { fun a() { } + fun b() } ``` @@ -986,17 +1011,23 @@ Rule id: `no-consecutive-blank-lines` (`standard` rule set) ```kotlin class C + data class DC(val v: Any) + interface I + object O ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin class C {} + data class DC(val v: Any) { } + interface I { } + object O{} ``` @@ -1094,7 +1125,7 @@ Rule id: `no-multi-spaces` (`standard` rule set) ## No semicolons -No semicolons (unless used to separate multiple statements on the same line). +Avoid using unnecessary semicolons. === "[:material-heart:](#) Ktlint" @@ -1128,7 +1159,6 @@ Rule id: `no-trailing-spaces` (`standard` rule set) ## No `Unit` as return type The `Unit` type is not allowed as return type of a function. -returns (`fun fn {}` instead of `fun fn: Unit {}`) === "[:material-heart:](#) Ktlint" @@ -1290,12 +1320,14 @@ Consistent spacing around colon. ```kotlin class A : B + class A2 : B2 ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin class A:B + class A2 : B2 ``` @@ -1331,12 +1363,13 @@ The end of line comment sign `//` should be preceded and followed by exactly a s var debugging = false // comment var debugging = false // comment var debugging = false // comment + fun main() { - System.out.println( // 123 - "test" + System.out.println( + // comment + "test", ) - } - // comment + } // comment ``` === "[:material-heart-off-outline:](#) Disallowed" @@ -1346,12 +1379,13 @@ The end of line comment sign `//` should be preceded and followed by exactly a s var debugging = false// comment var debugging = false //comment var debugging = false//comment + fun main() { - System.out.println(//123 + System.out.println( + //123 "test" ) - } - //comment + }//comment ``` Rule id: `comment-spacing` (`standard` rule set) @@ -1363,12 +1397,12 @@ Consistent spacing around curly braces. === "[:material-heart:](#) Ktlint" ```kotlin - val foo = if (true) { 0 } else { 1 } + val foo = bar { foo() } ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val foo = if (true){0}else{1} + val foo = bar{foo()} ``` Rule id: `curly-spacing` (`standard` rule set) @@ -1424,8 +1458,11 @@ Consistent spacing around the function return type. ```kotlin fun foo1() : String = "some-result" + fun foo2(): String = "some-result" + fun foo3():String = "some-result" + fun foo4(): String = "some-result" ``` @@ -1439,16 +1476,26 @@ Consistent spacing before start of function body. === "[:material-heart:](#) Ktlint" ```kotlin + // In case `ktlint_function_signature_body_expression_wrapping` is set to `default` or `multiline` fun foo1() = "some-result" + + // In case `ktlint_function_signature_body_expression_wrapping` is set to `always` fun foo2() = "some-result" + fun foo3() { // do something } + + // In case `ktlint_function_signature_body_expression_wrapping` is set to `default` or `multiline` fun bar1(): String = "some-result" + + // In case `ktlint_function_signature_body_expression_wrapping` is set to `always` fun bar2(): String = "some-result" + fun bar3(): String { + doSomething() return "some-result" } ``` @@ -1456,15 +1503,20 @@ Consistent spacing before start of function body. ```kotlin fun foo1()= "some-result" + fun foo2() = "some-result" + fun foo3() { // do something } + fun bar1(): String= "some-result" + fun bar2(): String = "some-result" + fun bar3(): String { return "some-result" @@ -1543,14 +1595,18 @@ Consistent spacing around keywords. ```kotlin fun main() { - if (true) {} + if (true) { + doSomething() + } } ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin fun main() { - if(true){} + if(true) { + doSomething() + } } ``` @@ -1633,7 +1689,8 @@ Consistent spacing inside the parameter list. === "[:material-heart:](#) Ktlint" ```kotlin - fun foo(a: Any ) = "some-result" + fun foo(a: Any) = "some-result" + fun foo() = "some-result" ``` === "[:material-heart-off-outline:](#) Disallowed" @@ -1656,6 +1713,7 @@ Consistent spacing around parenthesis. class Foo : Bar { constructor(string: String) : super() } + val foo1 = ((1 + 2) / 3) ``` === "[:material-heart-off-outline:](#) Disallowed" @@ -1664,6 +1722,7 @@ Consistent spacing around parenthesis. class Foo : Bar { constructor(string: String) : super () } + val foo1 = ( (1 + 2 ) / 3) ``` @@ -1752,6 +1811,7 @@ Spacing before and after the angle brackets of a type argument list. ```kotlin val res = ArrayList() + class B : A() { override fun x() = super.x() } @@ -1760,6 +1820,7 @@ Spacing before and after the angle brackets of a type argument list. ```kotlin val res = ArrayList < LintError > () + class B : A< T >() { override fun x() = super< A >.x() } @@ -1775,14 +1836,18 @@ Spacing after a type parameter list in function and class declarations. ```kotlin fun foo1(t: T) = "some-result" + fun foo2(t: T) = "some-result" + fun foo3(t: T) = "some-result" ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin fun foo1(t: T) = "some-result" + fun foo2(t: T) = "some-result" + funfoo3(t: T) = "some-result" ``` @@ -1796,14 +1861,18 @@ No spaces around unary operators. ```kotlin fun foo1(i: Int) = i++ + fun foo2(i: Int) = ++i + fun foo3(i: Int) = ++i ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin fun foo1(i: Int) = i ++ + fun foo2(i: Int) = ++ i + fun foo3(i: Int) = ++ i ``` @@ -1839,6 +1908,7 @@ Enforce consistent string template indentation for multiline string templates wh line1 line2 """.trimIndent() + fun foo() { // The opening """ can not be wrapped to next line as that would result in a compilation error return """ @@ -1854,6 +1924,7 @@ Enforce consistent string template indentation for multiline string templates wh line1 line2 """.trimIndent() + fun foo() { return """ line1 @@ -1877,20 +1948,22 @@ Consistent removal (default) or adding of trailing commas on call site. === "[:material-heart:](#) Ktlint" ```kotlin - FooWrapper( - Foo( - a = 3, - b = 4, - ), - ) + val foo = + FooWrapper( + Foo( + a = 3, + b = 4, + ), + ) ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - FooWrapper(Foo( - a = 3, - b = 4, - ),) // it's weird to insert "," between unwrapped (continued) parenthesis + val foo = + FooWrapper(Foo( + a = 3, + b = 4, + ),) // it's weird to insert "," between unwrapped (continued) parenthesis ``` !!! note @@ -1954,13 +2027,13 @@ An empty parentheses block before a lambda is redundant. === "[:material-heart:](#) Ktlint" ```kotlin - "some-string".count { it == '-' } + val foo = "some-string".count { it == '-' } ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - "some-string".count() { it == '-' } + val foo = "some-string".count() { it == '-' } ``` Rule id: `unnecessary-parentheses-before-trailing-lambda` (`standard` rule set) @@ -1974,19 +2047,21 @@ All arguments should be on the same line, or every argument should be on a separ === "[:material-heart:](#) Ktlint" ```kotlin - val x = f( - a, - b, - c - ) + val foo = + foo( + a, + b, + c, + ) ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val x = f( - a, - b, c - ) + val foo = + foo( + a, + b, c, + ) ``` Rule-id: `argument-list-wrapping` (`standard` rule set) @@ -1998,22 +2073,26 @@ When wrapping chained calls `.`, `?.` and `?:` should be placed on the next line === "[:material-heart:](#) Ktlint" ```kotlin - val foo = listOf(1, 2, 3) - .filter { it > 2 }!! - .takeIf { it.count() > 100 } - ?.sum() - val foobar = foo() - ?: bar + val foo = + listOf(1, 2, 3) + .filter { it > 2 }!! + .takeIf { it.count() > 100 } + ?.sum() + val foobar = + foo() + ?: bar ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val foo = listOf(1, 2, 3). - filter { it > 2 }!!. - takeIf { it.count() > 100 }?. - sum() - val foobar = foo() ?: - bar + val foo = + listOf(1, 2, 3). + filter { it > 2 }!!. + takeIf { it.count() > 100 }?. + sum() + val foobar = + foo() ?: + bar ``` Rule id: `chain-wrapping` (`standard` rule set) @@ -2025,7 +2104,7 @@ A block comment should start and end on a line that does not contain any other e === "[:material-heart:](#) Ktlint" ```kotlin - /* Some comment 1 */ + // Some comment 1 val foo1 = "foo1" val foo2 = "foo" // Some comment val foo3 = { /* no-op */ } @@ -2049,10 +2128,10 @@ Wraps the content receiver list to a separate line regardless of maximum line le === "[:material-heart:](#) Ktlint" ```kotlin - // ALways wrap regardless of whether max line length is set + // Always wrap regardless of whether max line length is set context(Foo) fun fooBar() - + // Wrap each context receiver to a separate line when the // entire context receiver list does not fit on a single line context( @@ -2060,7 +2139,7 @@ Wraps the content receiver list to a separate line regardless of maximum line le Foooooooooooooooooooooooooooooo2 ) fun fooBar() - + // Wrap each context receiver to a separate line when the // entire context receiver list does not fit on a single line. // Also, wrap each of it projection types in case a context @@ -2069,7 +2148,7 @@ Wraps the content receiver list to a separate line regardless of maximum line le context( Foooooooooooooooo< Foo, - Bar + Bar, > ) fun fooBar() @@ -2200,18 +2279,26 @@ When class/function signature doesn't fit on a single line, each parameter must === "[:material-heart:](#) Ktlint" ```kotlin + // If `ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than` equals + // `unset` the parameters are not wrapped as long as they fit on a single line class ClassA(paramA: String, paramB: String, paramC: String) + class ClassA( paramA: String, paramB: String, paramC: String ) + + // If `ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than` equals + // `unset` the parameters are not wrapped as long as they fit on a single line fun f(a: Any, b: Any, c: Any) + fun f( a: Any, b: Any, c: Any ) + fun foo( @Bar fooBar: FooBar ) @@ -2223,10 +2310,12 @@ When class/function signature doesn't fit on a single line, each parameter must paramA: String, paramB: String, paramC: String ) + fun f( a: Any, b: Any, c: Any ) + fun foo(@Bar fooBar: FooBar) ``` === "[:material-heart-off-outline:](#) Disallowed (non ktlint_official)"" @@ -2236,6 +2325,7 @@ When class/function signature doesn't fit on a single line, each parameter must paramA: String, paramB: String, paramC: String ) + fun f( a: Any, b: Any, c: Any @@ -2257,6 +2347,7 @@ When a function or class parameter doesn't fit on a single line, wrap the type o val fooooooooooooooooooooooooTooLong: Foo, ) + fun bar( fooooooooooooooooooooooooTooLong: Foo, @@ -2271,6 +2362,7 @@ When a function or class parameter doesn't fit on a single line, wrap the type o val fooooooooooooooooooooooooTooLong: Foo, ) + fun bar( fooooooooooooooooooooooooTooLong: Foo, @@ -2284,6 +2376,7 @@ When a function or class parameter doesn't fit on a single line, wrap the type o class Bar( val fooooooooooooooooooooooooTooLong: Foo, ) + fun bar( fooooooooooooooooooooooooooooTooLong: Foo, ) @@ -2325,11 +2418,14 @@ A function, class/object body or other block body statement has to be placed on // do something } } + class A { val a = 0 val b = 1 } + enum class FooBar1 { FOO, BAR } + enum class FooBar2 { FOO, BAR, @@ -2343,6 +2439,7 @@ A function, class/object body or other block body statement has to be placed on // do something } } + class A { val a = 0 val b = 1 } ``` @@ -2356,17 +2453,18 @@ Inserts missing newlines (for example between parentheses of a multi-line functi === "[:material-heart:](#) Ktlint" ```kotlin - val x = f( - a, - b, - c - ) + val foo = + foo( + a, + b, + c, + ) ``` === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val x = f( + val foo = foo( a, b, c) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index b75c306864..e7a5475a60 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -8,6 +8,8 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE +import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_DECLARATION +import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_LITERAL import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY_ACCESSOR import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN @@ -48,6 +50,7 @@ public class BlankLineBeforeDeclarationRule : CLASS, CLASS_INITIALIZER, FUN, + OBJECT_DECLARATION, PROPERTY, PROPERTY_ACCESSOR, -> @@ -126,6 +129,15 @@ public class BlankLineBeforeDeclarationRule : return } + if (node.elementType == OBJECT_DECLARATION && node.treeParent.elementType == OBJECT_LITERAL) { + // Allow: + // fun foo() = + // object : Foo() { + // // some declarations + // } + return + } + node .takeIf { it.psi is KtDeclaration } ?.takeIf { diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRuleTest.kt index cee323deb7..bcfb78a219 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRuleTest.kt @@ -3,6 +3,7 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue import com.pinterest.ktlint.test.KtLintAssertThat +import com.pinterest.ktlint.test.KtlintDocumentationTest import com.pinterest.ktlint.test.LintViolation import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -459,4 +460,43 @@ class BlankLineBeforeDeclarationRuleTest { """.trimIndent() blankLineBeforeDeclarationRuleAssertThat(code).hasNoLintViolations() } + + @KtlintDocumentationTest + fun `Issue 2284 - Given an object declaration preceded by another declaration`() { + val code = + """ + class C + data class DC(val v: Any) + interface I + object O + """.trimIndent() + val formattedCode = + """ + class C + + data class DC(val v: Any) + + interface I + + object O + """.trimIndent() + blankLineBeforeDeclarationRuleAssertThat(code) + .hasLintViolations( + LintViolation(2, 1, "Expected a blank line for this declaration"), + LintViolation(3, 1, "Expected a blank line for this declaration"), + LintViolation(4, 1, "Expected a blank line for this declaration"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Issue 2284 - Given an object declaration wrapped in object literal`() { + val code = + """ + fun foo() = + object : Foo() { + // some declarations + } + """.trimIndent() + blankLineBeforeDeclarationRuleAssertThat(code).hasNoLintViolations() + } }