Regarding `function-signature` rule: Is there a se...
# ktlint
d
Regarding
function-signature
rule: Is there a setting that would only format expressions starting on the same line as the function header (to prevent this)
Copy code
-    private fun buildProductNumber(product: ProductDTO) = when (product) {
-        is AccountDTO -> buildFullAccountNumber(product)
-        is LoanDTO -> buildFullLoanNumber(product)
-    }
but leave any other formatting intact if the body already starts on the new line (even if it would fit on a single line)?
Copy code
-    fun List<StatementDefinitionDTO>.toApi() =
-        StatementDefinitionsResponse(statementDefinitions = map { it.toApi() })
p
Please see docs for available settings. Otherwise please provide more samples with expected versus actual outputs. I cannot figure about what you mean exactly with above.
d
Yes, I read the docs. I would like a more lenient version the
multiline
mode, i.e. force wrapping only when multiline and keep anything else intact - especially do not force single line).
p
That would not always be consistent. Code below would be accepted:
Copy code
fun foo(bar: String) = bar

fun foo(bar: String) =
    bar
d
exactly, that's the point
p
Sorry, that goes agains what ktlint is try to achieve: consistent formatting.
d
I see, makes sense. Even though no linter could ever claim that it covers 100% of cases. There always will be (and perhaps should be) some room for customization so I don't think that everything must be necessarily covered. This case is a little complicated, though, because I see a strong reason to enforce the multiline rule for multiline expressions but not having a strong opinion in general on the singleline expressions, especially in cases like this:
Copy code
private fun <T, R, EMW, E : ApiError> responseDtoOr(
        response: T,
        status: GenericStatus<EMW>,
        dtoMapper: (T) -> R,
        errorBuilder: (List<EMW>) -> E,
    ): Result<R, E> = if (status.isOk) Ok(dtoMapper(response)) else Err(errorBuilder(status.errors))
p
Code above is accepted with
ktlint_function_signature_body_expression_wrapping
for
default
and
multiline
. As you said, you see value in wrapping multiline expression which means that you best can use
multiline
as setting for this configuration.
d
Not only it is accepted but it is forced, and that is the problem 🙂 Sorry for being not clear. I find this version quite unreadably therefore I would like this particular case to be formatted as:
Copy code
private fun <T, R, EMW, E : ApiError> responseDtoOr(
        response: T,
        status: GenericStatus<EMW>,
        dtoMapper: (T) -> R,
        errorBuilder: (List<EMW>) -> E,
    ): Result<R, E> = 
        if (status.isOk) Ok(dtoMapper(response)) else Err(errorBuilder(status.errors))
Perhaps, in the multiline mode, the single line body rule could trigger only in case the function header also fits on a single line. WDYT?
p
Ok, I think I get what you mean. Let me try to make it explicit with some more (simplified) code examples:
Copy code
private fun foo1(foo1: String, foo2: String) = foo1 + foo2
private fun foo2(foo1: String, foo2: String): String = foo1 + foo2
private fun fooooooooooo3(foo1: String, foo2: String) = foo1 + foo2
private fun foo4(foo1: String, foo2: String, foo3: String) = foo1 + foo2 + foo3
private fun foo5(foo1: String, foo2: String, foo3: String): String = foo1 + foo2 + foo3
and
.editorconfig
settings:
Copy code
max_line_length = 66
ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 3
the code will be formatted as:
Copy code
// Signature is allowed on single line as it has less than 3 parameters.
// Expression body and function signature fit on single line
private fun foo1(foo1: String, foo2: String) = foo1 + foo2

// ---new behavior---
// Signature is allowed on single line as it has less than 3 parameters.
// The expression body is wrapped to a new line because the function
// signature has an explicit return type
private fun foo2(foo1: String, foo2: String): String =
    foo1 + foo2

// Signature is allowed on single line as it has less than 3 parameters.
// Expression body does not fit on same line as function signature fit
// and will be wrapped to next line
private fun fooooooooooo3(foo1: String, foo2: String) =
    foo1 + foo2

// Signature will be forced to multiline as it has too many parameters
// The body expression starts on same line as the function signature
// does not have a return type
private fun foo4(
    foo1: String,
    foo2: String,
    foo3: String,
) = foo1 + foo2 + foo3

// ---new behavior---
// Signature will be forced to multiline as it has too many parameters
// The expression body is wrapped to a new line because the function
// signature has an explicit return type
private fun foo5(
    foo1: String,
    foo2: String,
    foo3: String,
): String =
    foo1 + foo2 + foo3
d
That would definitely be an improvement! Although I'm still partial to the different behaviour between explicit and implicit return types. IMO this just shouldn't make a difference. Even if you want it to make a difference then at least in the penultimate example the multiline signature should override this rule, i.e.
Copy code
private fun foo4(
    foo1: String,
    foo2: String,
    foo3: String,
) =
    foo1 + foo2 + foo3
p
I see your argument, but personally I find it quite ugly. I will sent out a poll in the Kotlin Developers group on LinkedIn. If a clear majority votes that this is superior, I will change the behavior like this. If both are like more or less equally, the behavior stay as it is right. This will not be added as a configuration setting.
d
That is fair 🙂 Just to make it absolutely clear: The only reason for the difference between your last two examples is the absence/presence of the explicit type, right?
p
Yes indeed