diff --git a/CHANGELOG.md b/CHANGELOG.md index 04be99080b..068f425d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Fix indent of delegated super type entry (`indent`) ([#1210](https://github.com/pinterest/ktlint/issues/1210)) - Improve indentation of closing quotes of a multiline raw string literal (`indent`) ([#1262](https://github.com/pinterest/ktlint/pull/1262)) - Fix false positive indentation (`parameter-list-wrapping`, `argument-list-wrapping`) ([#897](https://github.com/pinterest/ktlint/issues/897), [#1045](https://github.com/pinterest/ktlint/issues/1045), [#1119](https://github.com/pinterest/ktlint/issues/1119), [#1255](https://github.com/pinterest/ktlint/issues/1255), [#1267](https://github.com/pinterest/ktlint/issues/1267), [#1319](https://github.com/pinterest/ktlint/issues/1319), [#1320](https://github.com/pinterest/ktlint/issues/1320), [#1337](https://github.com/pinterest/ktlint/issues/1337) +- Force a single line function type inside a nullable type to a separate line when the max line length is exceeded (`parameter-list-wrapping`) ([#1255](https://github.com/pinterest/ktlint/issues/1255)) ### Changed - Update Kotlin version to `1.6.0` release diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt index 6d96cb4d54..26c6def709 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt @@ -51,11 +51,49 @@ class ParameterListWrappingRule : Rule("parameter-list-wrapping") { if (indentConfig.disabled) { return } + + node + .takeIf { it.elementType == NULLABLE_TYPE } + ?.takeUnless { + // skip when max line length not set or does not exceed max line length + maxLineLength <= 0 || (node.column - 1 + node.textLength) <= maxLineLength + }?.findChildByType(FUNCTION_TYPE) + ?.findChildByType(VALUE_PARAMETER_LIST) + ?.takeIf { it.findChildByType(VALUE_PARAMETER) != null } + ?.takeUnless { it.textContains('\n') } + ?.let { + node + .children() + .forEach { + when (it.elementType) { + LPAR -> { + emit( + it.startOffset, + "Parameter of nullable type should be on a separate line (unless the type fits on a single line)", + true + ) + if (autoCorrect) { + (it as LeafElement).upsertWhitespaceAfterMe("\n${indentConfig.indent}") + } + } + RPAR -> { + emit(it.startOffset, errorMessage(it), true) + if (autoCorrect) { + (it as LeafElement).upsertWhitespaceBeforeMe("\n") + } + } + } + } + } + if (node.elementType == VALUE_PARAMETER_LIST && // skip when there are no parameters node.firstChildNode?.treeNext?.elementType != RPAR && // skip lambda parameters - node.treeParent?.elementType != FUNCTION_LITERAL + node.treeParent?.elementType != FUNCTION_LITERAL && + // skip when function type is wrapped in a nullable type [which was already when processing the nullable + // type node itself. + !(node.treeParent.elementType == FUNCTION_TYPE && node.treeParent?.treeParent?.elementType == NULLABLE_TYPE) ) { // each parameter should be on a separate line if // - at least one of the parameters is diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRuleTest.kt index f947b94bc2..d596cc2f2e 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRuleTest.kt @@ -476,4 +476,28 @@ class ParameterListWrappingRuleTest { """.trimIndent() assertThat(ParameterListWrappingRule().lint(code)).isEmpty() } + + @Test + fun `Issue 1255 - Given a variable declaration for nullable function type which exceeds the max-line-length then wrap the function type to a new line`() { + val code = + """ + var changesListener: ((width: Double?, depth: Double?, length: Double?, area: Double?) -> Unit)? = null + """.trimIndent() + val formattedCode = + """ + var changesListener: ( + (width: Double?, depth: Double?, length: Double?, area: Double?) -> Unit + )? = null + """.trimIndent() + assertThat( + ParameterListWrappingRule().lint( + code, + userData = mapOf("max_line_length" to "80") + ) + ).containsExactly( + LintError(1, 22, "parameter-list-wrapping", "Parameter of nullable type should be on a separate line (unless the type fits on a single line)"), + LintError(1, 95, "parameter-list-wrapping", """Missing newline before ")"""") + ) + assertThat(ParameterListWrappingRule().format(code, userData = mapOf("max_line_length" to "80"))).isEqualTo(formattedCode) + } }