diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt new file mode 100644 index 0000000000..e82f4df45f --- /dev/null +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRule.kt @@ -0,0 +1,23 @@ +package com.github.shyiko.ktlint.ruleset.standard + +import com.github.shyiko.ktlint.core.Rule +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.lexer.KtTokens + +class NoLineBreakBeforeAssignmentRule : Rule("no-line-break-before-assignment") { + + override fun visit(node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { + if (node.elementType == KtTokens.EQ) { + val prevElement = node.treePrev?.psi + if (prevElement is PsiWhiteSpace && prevElement.text.contains("\n")) { + emit(node.startOffset, "Line break before assignment is not allowed", true) + if (autoCorrect) { + (node.treeNext?.psi as LeafPsiElement).replaceWithText(prevElement.text) + (prevElement as LeafPsiElement).replaceWithText(" ") + } + } + } + } +} diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt index 9fd9190c50..2070e5b25c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/StandardRuleSetProvider.kt @@ -28,6 +28,7 @@ class StandardRuleSetProvider : RuleSetProvider { SpacingAroundCurlyRule(), SpacingAroundKeywordRule(), SpacingAroundOperatorsRule(), - StringTemplateRule() + StringTemplateRule(), + NoLineBreakBeforeAssignmentRule() ) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt new file mode 100644 index 0000000000..55ac3e3990 --- /dev/null +++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoLineBreakBeforeAssignmentRuleTest.kt @@ -0,0 +1,69 @@ +package com.github.shyiko.ktlint.ruleset.standard + +import com.github.shyiko.ktlint.core.LintError +import com.github.shyiko.ktlint.test.format +import com.github.shyiko.ktlint.test.lint +import org.assertj.core.api.Assertions.assertThat +import org.testng.annotations.Test + +const val ruleId = "no-line-break-before-assignment" + +class NoLineBreakBeforeAssignmentRuleTest { + @Test + fun testAllPartsOnSameLineIsValid() { + assertThat(NoLineBreakBeforeAssignmentRule().lint( + """ + val valA = "" + """.trimIndent() + )).isEmpty() + } + + @Test + fun testLineBreakAfterAssignmentIsValid() { + assertThat(NoLineBreakBeforeAssignmentRule().lint( + """ + val valA = + "" + """.trimIndent() + )).isEmpty() + } + + @Test + fun testLineBreakBeforeAssignmentIsViolation() { + assertThat(NoLineBreakBeforeAssignmentRule().lint( + """ + val valA + = "" + """.trimIndent() + )).isEqualTo(listOf( + LintError(2, 7, ruleId, "Line break before assignment is not allowed") + )) + } + + @Test + fun testViolationInFunction() { + assertThat(NoLineBreakBeforeAssignmentRule().lint( + """ + fun funA() + = "" + """.trimIndent() + )).isEqualTo(listOf( + LintError(2, 7, ruleId, "Line break before assignment is not allowed") + )) + } + + @Test + fun testFixViolationByRemovingLineBreakFromLeftAndPutItOnRightSide() { + assertThat(NoLineBreakBeforeAssignmentRule().format( + """ + fun funA() + = "" + """.trimIndent() + )).isEqualTo( + """ + fun funA() = + "" + """.trimIndent() + ) + } +}