Skip to content

Commit

Permalink
Extract new rule no-blank-lines-in-chained-method-calls from no-conse…
Browse files Browse the repository at this point in the history
…cutive-blank-lines (#1469, #1248)

The 'no-consecutive-blank-lines' rule  no longer disallows a single blank line in a chained method call. Multiple consecutive blank lines are still prohibited. The new rule 'no-blank-lines-in-chained-method-calls' also prohibits single blank lines in chained method call.

Closes #1248
  • Loading branch information
seadowg authored May 25, 2022
1 parent c083a97 commit d4ad6b6
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ An AssertJ style API for testing KtLint rules ([#1444](https://github.com/pinter
- Add experimental rule for rewriting the function signature (`function-signature`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))

### Fixed
- Move disallowing blank lines in chained method calls from `no-consecutive-blank-lines` to new rule (`no-blank-lines-in-chained-method-calls`) ([#1248](https://github.com/pinterest/ktlint/issues/1248))
- Fix check of spacing in the receiver type of an anonymous function ([#1440](https://github.com/pinterest/ktlint/issues/1440))
- Allow comment on same line as super class in class declaration `wrapping` ([#1457](https://github.com/pinterest/ktlint/pull/1457))
- Fix formatting of a property delegate with a dot-qualified-expression `indent` ([#1340](https://github.com/pinterest/ktlint/ssues/1340))
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ It's also [easy to create your own](#creating-a-reporter).
- `max-line-length`: Ensures that lines do not exceed the given length of `.editorconfig` property `max_line_length` (see [EditorConfig](#editorconfig) section for more). This rule does not apply in a number of situations. For example, in the case a line exceeds the maximum line length due to and comment that disables ktlint rules than that comment is being ignored when validating the length of the line. The `.editorconfig` property `ktlint_ignore_back_ticked_identifier` can be set to ignore identifiers which are enclosed in backticks, which for example is very useful when you want to allow longer names for unit tests.
- `modifier-order`: Consistent order of modifiers
- `no-blank-line-before-rbrace`: No blank lines before `}`
- `no-blank-lines-in-chained-method-calls`: No blank lines in chained method expressions
- `no-consecutive-blank-lines`: No consecutive blank lines
- `no-empty-class-body`: No empty (`{}`) class bodies
- `no-line-break-after-else`: Disallows line breaks after the else keyword if that could lead to confusion, for example:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.pinterest.ktlint.ruleset.standard

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
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

public class NoBlankLinesInChainedMethodCallsRule : Rule("no-blank-lines-in-chained-method-calls") {
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
val isBlankLine = node is PsiWhiteSpace && node.getText().contains("\n\n")
if (isBlankLine && node.treeParent.elementType == DOT_QUALIFIED_EXPRESSION) {
emit(node.startOffset + 1, "Needless blank line(s)", true)

if (autoCorrect) {
(node as LeafPsiElement).rawReplaceWithText("\n" + node.getText().split("\n\n")[1])
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.pinterest.ktlint.ruleset.standard

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR
import com.pinterest.ktlint.core.ast.nextLeaf
Expand All @@ -25,20 +24,21 @@ public class NoConsecutiveBlankLinesRule : Rule("no-consecutive-blank-lines") {
if (lfcount < 2) {
return
}

val eof = node.nextLeaf() == null
val prevNode = node.treePrev
val betweenClassAndPrimaryConstructor = prevNode.elementType == IDENTIFIER &&
prevNode.treeParent.elementType == CLASS &&
node.treeNext.elementType == PRIMARY_CONSTRUCTOR
val inDotQualifiedExpression = node.treeParent.elementType == DOT_QUALIFIED_EXPRESSION && lfcount > 1
if (lfcount > 2 || eof || betweenClassAndPrimaryConstructor || inDotQualifiedExpression) {

if (lfcount > 2 || eof || betweenClassAndPrimaryConstructor) {
val split = text.split("\n")
emit(node.startOffset + split[0].length + split[1].length + 2, "Needless blank line(s)", true)
if (autoCorrect) {
val newText = buildString {
append(split.first())
append("\n")
if (!eof && !betweenClassAndPrimaryConstructor && !inDotQualifiedExpression) append("\n")
if (!eof && !betweenClassAndPrimaryConstructor) append("\n")
append(split.last())
}
(node as LeafPsiElement).rawReplaceWithText(newText)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class StandardRuleSetProvider : RuleSetProvider {
ModifierOrderRule(),
NoBlankLineBeforeRbraceRule(),
NoConsecutiveBlankLinesRule(),
NoBlankLinesInChainedMethodCallsRule(),
NoEmptyClassBodyRule(),
NoLineBreakAfterElseRule(),
NoLineBreakBeforeAssignmentRule(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.pinterest.ktlint.ruleset.standard

import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThat
import org.junit.jupiter.api.Test

class NoBlankLinesInChainedMethodCallsRuleTest {
private val noBlankLinesInChainedMethodCallsRuleAssertThat = NoBlankLinesInChainedMethodCallsRule().assertThat()

@Test
fun `single blank line in dot qualified expression returns lint error`() {
val code =
"""
fun foo(inputText: String) {
inputText
.toLowerCase()
}
""".trimIndent()
val formattedCode =
"""
fun foo(inputText: String) {
inputText
.toLowerCase()
}
""".trimIndent()
noBlankLinesInChainedMethodCallsRuleAssertThat(code)
.hasLintViolation(3, 1, "Needless blank line(s)")
.isFormattedAs(formattedCode)
}

@Test
fun `single blank line between statements does not return lint error`() {
val code =
"""
fun foo(inputText: String) {
bar()
bar()
}
""".trimIndent()
noBlankLinesInChainedMethodCallsRuleAssertThat(code).hasNoLintViolations()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.pinterest.ktlint.ruleset.standard
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThat
import com.pinterest.ktlint.test.LintViolation
import com.pinterest.ktlint.test.MULTILINE_STRING_QUOTE
import com.pinterest.ktlint.test.format
import com.pinterest.ktlint.test.lint
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
Expand Down Expand Up @@ -176,25 +175,41 @@ class NoConsecutiveBlankLinesRuleTest {
}

@Test
fun `should remove line in dot qualified expression`() {
assertThat(
NoConsecutiveBlankLinesRule().format(
"""
fun foo(inputText: String) {
inputText
fun `Given single blank line in dot qualified expression should not return lint errors`() {
val code =
"""
fun foo(inputText: String) {
inputText
.toLowerCase()
}
""".trimIndent()

.toLowerCase()
}
""".trimIndent()
)
).isEqualTo(
noConsecutiveBlankLinesRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given multiple blank line in dot qualified expression should return lint error`() {
val code =
"""
fun foo(inputText: String) {
inputText
.toLowerCase()
}
""".trimIndent()
)

noConsecutiveBlankLinesRuleAssertThat(code)
.hasLintViolations(LintViolation(4, 1, "Needless blank line(s)"))
.isFormattedAs(
"""
fun foo(inputText: String) {
inputText
.toLowerCase()
}
""".trimIndent()
)
}
}

0 comments on commit d4ad6b6

Please sign in to comment.