Skip to content

Commit

Permalink
Merge pull request #2568 from pinterest/2454-argument-list-wrapping
Browse files Browse the repository at this point in the history
Run argument-list-wrapping after function-signature
  • Loading branch information
paul-dingemans authored Feb 27, 2024
2 parents 913887d + 6795977 commit 773fd1d
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class FormatReporterProvider : ReporterProviderV2<FormatReporter> {
private fun String.emptyOrTrue() = this == "" || this == "true"

private fun getColor(color: String?): Color =
Color.values().firstOrNull {
Color.entries.firstOrNull {
it.name == color
} ?: throw IllegalArgumentException("Invalid color parameter.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class PlainReporterProvider : ReporterProviderV2<PlainReporter> {
private fun String.emptyOrTrue() = this == "" || this == "true"

private fun getColor(colorInput: String?): Color =
Color.values().firstOrNull {
Color.entries.firstOrNull {
it.name == colorInput
} ?: throw IllegalArgumentException("Invalid color parameter.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public val CODE_STYLE_PROPERTY_TYPE: PropertyType.LowerCasingPropertyType<CodeSt
"The code style ('ktlint_official', 'intellij_idea' or 'android_studio') to be applied. By default the 'ktlint_official' code " +
"style is used",
SafeEnumValueParser(CodeStyleValue::class.java),
CodeStyleValue.values().map { it.name }.toSet(),
CodeStyleValue.entries.map { it.name }.toSet(),
)

public val CODE_STYLE_PROPERTY: EditorConfigProperty<CodeStyleValue> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public val RULE_EXECUTION_PROPERTY_TYPE: PropertyType.LowerCasingPropertyType<Ru
"When enabled, rule execution is allowed. This property can de defined at different levels like an entire ruleset, a specific " +
"rule or a specific property of the rule.",
SafeEnumValueParser(RuleExecution::class.java),
RuleExecution.values().map { it.name }.toSet(),
RuleExecution.entries.map { it.name }.toSet(),
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ class EditorConfigTest {
name,
"",
SafeEnumValueParser(RuleExecution::class.java),
RuleExecution.values().map { it.name }.toSet(),
RuleExecution.entries.map { it.name }.toSet(),
),
defaultValue = RuleExecution.enabled,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SafeEnumValueParserTest {
"some-property-type",
null,
SafeEnumValueParser(SomePropertyType::class.java),
SomePropertyType.values().map { it.name }.toSet(),
SomePropertyType.entries.map { it.name }.toSet(),
)

val actual = propertyType.parse(" value2 ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class ArgumentListWrappingRule :
RunAfterRule(VALUE_ARGUMENT_COMMENT_RULE_ID, REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED),
RunAfterRule(WRAPPING_RULE_ID, REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED),
RunAfterRule(CLASS_SIGNATURE_RULE_ID, REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED),
RunAfterRule(FUNCTION_SIGNATURE_RULE_ID, REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED),
),
usesEditorConfigProperties =
setOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevSibling
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe
Expand Down Expand Up @@ -733,12 +732,6 @@ public class FunctionSignatureRule :
.count { it.elementType == VALUE_PARAMETER }
}

private fun ASTNode.getLastNodeOfFunctionSignatureWithBlockBody(): ASTNode? =
this
.findChildByType(BLOCK)
?.firstChildNode
?.prevCodeLeaf()

public companion object {
private const val FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY_UNSET = Int.MAX_VALUE
public val FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY: EditorConfigProperty<Int> =
Expand Down Expand Up @@ -780,7 +773,7 @@ public class FunctionSignatureRule :
"line as the function signature. Use 'multiline' to force wrapping of body expressions that " +
"consists of multiple line. Use 'always' to force wrapping of body expression always.",
EnumValueParser(FunctionBodyExpressionWrapping::class.java),
FunctionBodyExpressionWrapping.values().map { it.name }.toSet(),
FunctionBodyExpressionWrapping.entries.map { it.name }.toSet(),
),
defaultValue = default,
ktlintOfficialCodeStyleDefaultValue = multiline,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package com.pinterest.ktlint.ruleset.standard.rules
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.MAX_LINE_LENGTH_PROPERTY
import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider
import com.pinterest.ktlint.ruleset.standard.rules.ArgumentListWrappingRule.Companion.IGNORE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY
import com.pinterest.ktlint.ruleset.standard.rules.ClassSignatureRule.Companion.FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY
import com.pinterest.ktlint.ruleset.standard.rules.FunctionSignatureRule.Companion.FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY
import com.pinterest.ktlint.ruleset.standard.rules.FunctionSignatureRule.FunctionBodyExpressionWrapping
import com.pinterest.ktlint.ruleset.standard.rules.FunctionSignatureRule.FunctionBodyExpressionWrapping.default
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.EOL_CHAR
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.MAX_LINE_LENGTH_MARKER
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRuleBuilder
import com.pinterest.ktlint.test.LintViolation
import com.pinterest.ktlint.test.MULTILINE_STRING_QUOTE
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 com.pinterest.ktlint.ruleset.standard.rules.ClassSignatureRule.Companion.FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY as CLASS_SIGNATURE_FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY

class ArgumentListWrappingRuleTest {
private val argumentListWrappingRuleAssertThat =
Expand Down Expand Up @@ -931,11 +936,12 @@ class ArgumentListWrappingRuleTest {
// body
}
""".trimIndent()
@Suppress("ktlint:standard:max-line-length")
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { ClassSignatureRule() }
.addRequiredRuleProviderDependenciesFrom(StandardRuleSetProvider())
.withEditorConfigOverride(FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY to 4)
.withEditorConfigOverride(CLASS_SIGNATURE_FORCE_MULTILINE_WHEN_PARAMETER_COUNT_GREATER_OR_EQUAL_THAN_PROPERTY to 4)
.hasLintViolationForAdditionalRule(5, 37, "Super type should start on a newline")
.hasLintViolations(
LintViolation(5, 44, "Argument should be on a separate line (unless all arguments can fit a single line)"),
Expand Down Expand Up @@ -1010,4 +1016,208 @@ class ArgumentListWrappingRuleTest {
LintViolation(2, 45, "Exceeded max line length (44)", false),
)
}

@Nested
inner class `Issue 2450 - Run 'argument-list-wrapping' after 'function-signature'` {
@Nested
inner class `Given default body expression wrapping` {
@Test
fun `Given a single line function signature`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo = Foo.baz(a = "looooooooooooooooooooong", b = "looooooooooooooooooooong")
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo = Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.isFormattedAs(formattedCode)
}

@Test
fun `Given a multiline function signature`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
bar1: String,
bar2: String,
): Foo = Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong",
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.hasNoLintViolations()
}

@Test
fun `Given a multiline function signature that should be single line`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
bar: String
): Foo = Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo =
Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.hasNoLintViolationsExceptInAdditionalRules()
.isFormattedAs(formattedCode)
}

@Test
fun `Given function and body which do not need wrapping as line length is not exceeded`() {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): String =
Foo.baz(a = "a", b = "b")
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to default)
.hasNoLintViolations()
}
}

@Nested
inner class `Given non-default body expression wrapping` {
@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionBodyExpressionWrapping::class,
mode = EnumSource.Mode.EXCLUDE,
names = ["default"],
)
fun `Given a single line function signature`(functionBodyExpressionWrapping: FunctionBodyExpressionWrapping) {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo = Foo.baz(a = "looooooooooooooooooooong", b = "looooooooooooooooooooong")
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo =
Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to functionBodyExpressionWrapping)
.isFormattedAs(formattedCode)
}

@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionBodyExpressionWrapping::class,
mode = EnumSource.Mode.EXCLUDE,
names = ["default"],
)
fun `Given a multiline function signature`(functionBodyExpressionWrapping: FunctionBodyExpressionWrapping) {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
bar1: String,
bar2: String,
): Foo = Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong",
)
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
bar1: String,
bar2: String,
): Foo =
Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong",
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to functionBodyExpressionWrapping)
.hasNoLintViolationsExceptInAdditionalRules()
.isFormattedAs(formattedCode)
}

@ParameterizedTest(name = "bodyExpressionWrapping: {0}")
@EnumSource(
value = FunctionBodyExpressionWrapping::class,
mode = EnumSource.Mode.EXCLUDE,
names = ["default"],
)
fun `Given a multiline function signature that should be single line`(
functionBodyExpressionWrapping: FunctionBodyExpressionWrapping,
) {
val code =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(
bar: String
): Foo = Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
val formattedCode =
"""
// $MAX_LINE_LENGTH_MARKER $EOL_CHAR
fun foo(bar: String): Foo =
Foo.baz(
a = "looooooooooooooooooooong",
b = "looooooooooooooooooooong"
)
""".trimIndent()
argumentListWrappingRuleAssertThat(code)
.setMaxLineLength()
.addAdditionalRuleProvider { FunctionSignatureRule() }
.addAdditionalRuleProvider { IndentationRule() }
.withEditorConfigOverride(FUNCTION_BODY_EXPRESSION_WRAPPING_PROPERTY to functionBodyExpressionWrapping)
.hasNoLintViolationsExceptInAdditionalRules()
.isFormattedAs(formattedCode)
}
}
}
}

0 comments on commit 773fd1d

Please sign in to comment.