From 557c5ad2aff1e5f41842bb7ddad2d4cbfcb48ba0 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sat, 5 Feb 2022 21:15:16 +0100 Subject: [PATCH 1/3] Add new experimental rule function-spacing Lints and formats the spacing after the fun keyword. This rule is required to create a rule which can rewrite the function signature automatically as is described in #1341 --- .../ExperimentalRuleSetProvider.kt | 3 +- .../experimental/FunctionSpacingRule.kt | 34 +++++++++++++++ .../experimental/FunctionSpacingRuleTest.kt | 42 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt create mode 100644 ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt index fa3f9aedd5..bb3f46638f 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt @@ -21,6 +21,7 @@ public class ExperimentalRuleSetProvider : RuleSetProvider { SpacingAroundAngleBracketsRule(), SpacingAroundUnaryOperatorRule(), AnnotationSpacingRule(), - UnnecessaryParenthesesBeforeTrailingLambdaRule() + UnnecessaryParenthesesBeforeTrailingLambdaRule(), + FunctionSpacingRule() ) } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt new file mode 100644 index 0000000000..f5c988af39 --- /dev/null +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt @@ -0,0 +1,34 @@ +package com.pinterest.ktlint.ruleset.experimental + +import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.ast.ElementType +import com.pinterest.ktlint.core.ast.ElementType.FUN_KEYWORD +import com.pinterest.ktlint.core.ast.nextLeaf +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement + +/** + * Lints and formats the spacing after the fun keyword + */ +public class FunctionSpacingRule : Rule("function-spacing") { + override fun visit( + node: ASTNode, + autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit + ) { + node + .takeIf { it.elementType == FUN_KEYWORD } + ?.nextLeaf(includeEmpty = true) + ?.takeIf { it.elementType == ElementType.WHITE_SPACE && it.text != " " } + ?.let { whiteSpaceAfterFunKeyword -> + emit( + whiteSpaceAfterFunKeyword.startOffset, + "Single space expected after the fun keyword", + true + ) + if (autoCorrect) { + (whiteSpaceAfterFunKeyword as LeafPsiElement).rawReplaceWithText(" ") + } + } + } +} diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt new file mode 100644 index 0000000000..e9f035d308 --- /dev/null +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt @@ -0,0 +1,42 @@ +package com.pinterest.ktlint.ruleset.experimental + +import com.pinterest.ktlint.core.LintError +import com.pinterest.ktlint.test.format +import com.pinterest.ktlint.test.lint +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class FunctionSpacingRuleTest { + @Test + fun `Given a function signature with multiple spaces between the fun keyword and the function name then remove the redundant spaces`() { + val code = + """ + fun foo() = "some-result" + """.trimIndent() + val formattedCode = + """ + fun foo() = "some-result" + """.trimIndent() + assertThat(FunctionSpacingRule().lint(code)).containsExactly( + LintError(1, 4, "function-spacing", "Single space expected after the fun keyword") + ) + assertThat(FunctionSpacingRule().format(code)).isEqualTo(formattedCode) + } + + @Test + fun `Given a function signature with a newline between the fun keyword and the function name then remove the redundant newline`() { + val code = + """ + fun + foo() = "some-result" + """.trimIndent() + val formattedCode = + """ + fun foo() = "some-result" + """.trimIndent() + assertThat(FunctionSpacingRule().lint(code)).containsExactly( + LintError(1, 4, "function-spacing", "Single space expected after the fun keyword") + ) + assertThat(FunctionSpacingRule().format(code)).isEqualTo(formattedCode) + } +} From e0138a345222dcaf887d5a7bd34a2242b0decf0c Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sat, 5 Feb 2022 21:21:14 +0100 Subject: [PATCH 2/3] Rename rule, update changelog and readme --- CHANGELOG.md | 1 + README.md | 1 + .../experimental/ExperimentalRuleSetProvider.kt | 2 +- ...tionSpacingRule.kt => FunKeywordSpacingRule.kt} | 2 +- ...ingRuleTest.kt => FunKeywordSpacingRuleTest.kt} | 14 +++++++------- 5 files changed, 11 insertions(+), 9 deletions(-) rename ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/{FunctionSpacingRule.kt => FunKeywordSpacingRule.kt} (94%) rename ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/{FunctionSpacingRuleTest.kt => FunKeywordSpacingRuleTest.kt} (65%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04be99080b..3b51ade09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Use Gradle JVM toolchain with language version 8 to compile the project - Basic tests for CLI ([#540](https://github.com/pinterest/ktlint/issues/540)) - Add experimental rule for unnecessary parentheses in function call followed by lambda ([#1068](https://github.com/pinterest/ktlint/issues/1068)) +- Add rule to check spacing after fun keyword (`fun-keyword-spacing`) ([#1362](https://github.com/pinterest/ktlint/pull/1362)) ### Fixed - Fix indentation of function literal ([#1247](https://github.com/pinterest/ktlint/issues/1247)) diff --git a/README.md b/README.md index 22d655f497..851e38cf4c 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ by passing the `--experimental` flag to `ktlint`. - No spaces around unary operators (id: `experimental:unary-op-spacing`) - Declarations with annotations should be separated by a blank line (id: `experimental:spacing-between-declarations-with-annotations`) - Declarations with comments should be separated by a blank line (id: `experimental:spacing-between-declarations-with-comments`) +- Spacing after the fun keyword (`experimental:fun-keyword-spacing`) ## EditorConfig diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt index bb3f46638f..d1e7233c2c 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ExperimentalRuleSetProvider.kt @@ -22,6 +22,6 @@ public class ExperimentalRuleSetProvider : RuleSetProvider { SpacingAroundUnaryOperatorRule(), AnnotationSpacingRule(), UnnecessaryParenthesesBeforeTrailingLambdaRule(), - FunctionSpacingRule() + FunKeywordSpacingRule() ) } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRule.kt similarity index 94% rename from ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt rename to ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRule.kt index f5c988af39..638a1befc2 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRule.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement /** * Lints and formats the spacing after the fun keyword */ -public class FunctionSpacingRule : Rule("function-spacing") { +public class FunKeywordSpacingRule : Rule("fun-keyword-spacing") { override fun visit( node: ASTNode, autoCorrect: Boolean, diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt similarity index 65% rename from ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt rename to ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt index e9f035d308..b4fe077852 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt @@ -6,7 +6,7 @@ import com.pinterest.ktlint.test.lint import org.assertj.core.api.Assertions.assertThat import org.junit.Test -class FunctionSpacingRuleTest { +class FunKeywordSpacingRuleTest { @Test fun `Given a function signature with multiple spaces between the fun keyword and the function name then remove the redundant spaces`() { val code = @@ -17,10 +17,10 @@ class FunctionSpacingRuleTest { """ fun foo() = "some-result" """.trimIndent() - assertThat(FunctionSpacingRule().lint(code)).containsExactly( - LintError(1, 4, "function-spacing", "Single space expected after the fun keyword") + assertThat(FunKeywordSpacingRule().lint(code)).containsExactly( + LintError(1, 4, "fun-keyword-spacing", "Single space expected after the fun keyword") ) - assertThat(FunctionSpacingRule().format(code)).isEqualTo(formattedCode) + assertThat(FunKeywordSpacingRule().format(code)).isEqualTo(formattedCode) } @Test @@ -34,9 +34,9 @@ class FunctionSpacingRuleTest { """ fun foo() = "some-result" """.trimIndent() - assertThat(FunctionSpacingRule().lint(code)).containsExactly( - LintError(1, 4, "function-spacing", "Single space expected after the fun keyword") + assertThat(FunKeywordSpacingRule().lint(code)).containsExactly( + LintError(1, 4, "fun-keyword-spacing", "Single space expected after the fun keyword") ) - assertThat(FunctionSpacingRule().format(code)).isEqualTo(formattedCode) + assertThat(FunKeywordSpacingRule().format(code)).isEqualTo(formattedCode) } } From 1278df3b2fb8b1722a28ccfa8429693a1987c4dc Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Tue, 8 Mar 2022 20:45:39 +0100 Subject: [PATCH 3/3] Migrate to JUnit5 --- .../ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt index b4fe077852..e932c4cc4c 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunKeywordSpacingRuleTest.kt @@ -4,7 +4,7 @@ import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.test.format import com.pinterest.ktlint.test.lint import org.assertj.core.api.Assertions.assertThat -import org.junit.Test +import org.junit.jupiter.api.Test class FunKeywordSpacingRuleTest { @Test