Skip to content

Commit

Permalink
Convert a single line block comment to an EOL comment if not preceded…
Browse files Browse the repository at this point in the history
… or followed by another code element on the same line

Closes #1941

Ignore a block comment inside a single line block `comment-wrapping`
Closes #1942
  • Loading branch information
paul-dingemans committed Apr 15, 2023
1 parent 840832c commit 795c1dd
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 105 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ if (node.isRoot()) {
* Update Kotlin development version to `1.8.20` and Kotlin version to `1.8.20`.
* Revert to matrix build to speed up build, especially for the Windows related build ([#1787](https://github.com/pinterest/ktlint/pull/1787))
* For the new code style `ktlint_official`, do not allow wildcard imports `java.util` and `kotlinx.android.synthetic` by default. Important: `.editorconfig` property `ij_kotlin_packages_to_use_import_on_demand` needs to be set to value `unset` in order to enforce IntelliJ IDEA default formatter to not generate wildcard imports `no-wildcard-imports` ([#1797](https://github.com/pinterest/ktlint/issues/1797))
* Convert a single line block comment to an EOL comment if not preceded or followed by another code element on the same line `comment-wrapping` ([#1941](https://github.com/pinterest/ktlint/issues/1941))
* Ignore a block comment inside a single line block `comment-wrapping` ([#1942](https://github.com/pinterest/ktlint/issues/1942))

## [0.48.2] - 2023-01-21

Expand Down
1 change: 1 addition & 0 deletions docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ A block comment should start and end on a line that does not contain any other e
/* Some comment 1 */
val foo1 = "foo1"
val foo2 = "foo" // Some comment
val foo3 = { /* no-op */ }
```
=== "[:material-heart-off-outline:](#) Disallowed"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ package com.pinterest.ktlint.ruleset.standard.rules

import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.EOL_COMMENT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE
import com.pinterest.ktlint.rule.engine.core.api.RuleId
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf
import com.pinterest.ktlint.rule.engine.core.api.hasNewLineInClosedRange
import com.pinterest.ktlint.rule.engine.core.api.indent
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe
import com.pinterest.ktlint.ruleset.standard.StandardRule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiCommentImpl
import org.jetbrains.kotlin.psi.psiUtil.leaves

/**
* Checks external wrapping of block comments. Wrapping inside the comment is not altered. A block comment following another element on the
Expand All @@ -35,19 +41,33 @@ public class CommentWrappingRule :
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
) {
if (node.elementType == BLOCK_COMMENT) {
val nonIndentLeafOnSameLinePrecedingBlockComment =
val beforeBlockComment =
node
.prevLeaf()
?.takeIf { isNonIndentLeafOnSameLine(it) }
val nonIndentLeafOnSameLineFollowingBlockComment =
.leaves(false)
.takeWhile { it.isWhiteSpace() && !it.textContains('\n') }
.firstOrNull()
?: node.firstChildLeafOrSelf()
val afterBlockComment =
node
.nextLeaf()
?.takeIf { isNonIndentLeafOnSameLine(it) }
.leaves()
.takeWhile { it.isWhiteSpace() && !it.textContains('\n') }
.firstOrNull()
?: node.lastChildLeafOrSelf()

if (nonIndentLeafOnSameLinePrecedingBlockComment != null &&
nonIndentLeafOnSameLineFollowingBlockComment != null
if (!node.textContains('\n') &&
beforeBlockComment.prevLeaf().isWhitespaceWithNewlineOrNull() &&
afterBlockComment.nextLeaf().isWhitespaceWithNewlineOrNull()
) {
if (hasNewLineInClosedRange(nonIndentLeafOnSameLinePrecedingBlockComment, nonIndentLeafOnSameLineFollowingBlockComment)) {
emit(node.startOffset, "A single line block comment must be replaced with an EOL comment", true)
if (autoCorrect) {
node.replaceWithEndOfLineComment()
}
}

if (!beforeBlockComment.prevLeaf().isWhitespaceWithNewlineOrNull() &&
!afterBlockComment.nextLeaf().isWhitespaceWithNewlineOrNull()
) {
if (hasNewLineInClosedRange(beforeBlockComment, afterBlockComment)) {
// Do not try to fix constructs like below:
// val foo = "foo" /*
// some comment
Expand All @@ -58,48 +78,57 @@ public class CommentWrappingRule :
"disallowed",
false,
)
} else if (beforeBlockComment.prevLeaf()?.elementType == LBRACE &&
afterBlockComment.nextLeaf()?.elementType == RBRACE
) {
// Allow single line blocks containing a block comment
// val foo = { /* no-op */ }
return
} else {
// Do not try to fix constructs like below:
// val foo /* some comment */ = "foo"
emit(
node.startOffset,
"A block comment in between other elements on the same line is disallowed",
false,
)
emit(node.startOffset, "A block comment in between other elements on the same line is disallowed", false)
}
return
}

nonIndentLeafOnSameLinePrecedingBlockComment
?.precedesBlockCommentOnSameLine(node, emit, autoCorrect)

nonIndentLeafOnSameLineFollowingBlockComment
?.followsBlockCommentOnSameLine(node, emit, autoCorrect)
}
}
beforeBlockComment
.prevLeaf()
.takeIf { !it.isWhitespaceWithNewlineOrNull() }
?.let {
if (node.textContains('\n')) {
// It can not be autocorrected as it might depend on the situation and code style what is preferred.
emit(
node.startOffset,
"A block comment after any other element on the same line must be separated by a new line",
false,
)
} else {
emit(
node.startOffset,
"A single line block comment after a code element on the same line must be replaced with an EOL comment",
true,
)
if (autoCorrect) {
node.upsertWhitespaceBeforeMe(" ")
node.replaceWithEndOfLineComment()
}
}
}

private fun ASTNode.precedesBlockCommentOnSameLine(
blockCommentNode: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean,
) {
val leafAfterBlockComment = blockCommentNode.nextLeaf()
if (!blockCommentNode.textContains('\n') && leafAfterBlockComment.isLastElementOnLine()) {
emit(
startOffset,
"A single line block comment after a code element on the same line must be replaced with an EOL comment",
true,
)
if (autoCorrect) {
blockCommentNode.replaceWithEndOfLineComment()
}
} else {
// It can not be autocorrected as it might depend on the situation and code style what is preferred.
emit(
blockCommentNode.startOffset,
"A block comment after any other element on the same line must be separated by a new line",
false,
)
afterBlockComment
.nextLeaf()
.takeIf { !it.isWhitespaceWithNewlineOrNull() }
?.let { nextLeaf ->
emit(
nextLeaf.startOffset,
"A block comment may not be followed by any other element on that same line",
true
)
if (autoCorrect) {
nextLeaf.upsertWhitespaceBeforeMe(node.indent())
}
}
}
}

Expand All @@ -110,20 +139,8 @@ public class CommentWrappingRule :
rawRemove()
}

private fun ASTNode.followsBlockCommentOnSameLine(
blockCommentNode: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean,
) {
emit(startOffset, "A block comment may not be followed by any other element on that same line", true)
if (autoCorrect) {
this.upsertWhitespaceBeforeMe(blockCommentNode.indent())
}
}

private fun isNonIndentLeafOnSameLine(it: ASTNode) = it.elementType != WHITE_SPACE || !it.textContains('\n')

private fun ASTNode?.isLastElementOnLine() = this == null || (elementType == WHITE_SPACE && textContains('\n'))
private fun ASTNode?.isWhitespaceWithNewlineOrNull() =
this == null || this.isWhiteSpaceWithNewline()
}

public val COMMENT_WRAPPING_RULE_ID: RuleId = CommentWrappingRule().ruleId

0 comments on commit 795c1dd

Please sign in to comment.