-
Notifications
You must be signed in to change notification settings - Fork 512
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wrapping block and kdoc comments (#1403)
Add new experimental rules for wrapping of block comments and KDoc comments Closes #1329
- Loading branch information
1 parent
c4ae318
commit 6c74bcb
Showing
9 changed files
with
513 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
...rimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/CommentWrappingRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package com.pinterest.ktlint.ruleset.experimental | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties | ||
import com.pinterest.ktlint.core.api.FeatureInAlphaState | ||
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties | ||
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT | ||
import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT | ||
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE | ||
import com.pinterest.ktlint.core.ast.lineIndent | ||
import com.pinterest.ktlint.core.ast.lineNumber | ||
import com.pinterest.ktlint.core.ast.nextLeaf | ||
import com.pinterest.ktlint.core.ast.prevLeaf | ||
import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe | ||
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 | ||
|
||
/** | ||
* Checks external wrapping of block comments. Wrapping inside the comment is not altered. A block comment following | ||
* another element on the same line is replaced with an EOL comment, if possible. | ||
*/ | ||
@OptIn(FeatureInAlphaState::class) | ||
public class CommentWrappingRule : | ||
Rule("comment-wrapping"), | ||
UsesEditorConfigProperties { | ||
override val editorConfigProperties: List<UsesEditorConfigProperties.EditorConfigProperty<*>> = | ||
listOf( | ||
DefaultEditorConfigProperties.indentSizeProperty, | ||
DefaultEditorConfigProperties.indentStyleProperty | ||
) | ||
|
||
override fun visit( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.elementType == BLOCK_COMMENT) { | ||
val nonIndentLeafOnSameLinePrecedingBlockComment = | ||
node | ||
.prevLeaf() | ||
?.takeIf { isNonIndentLeafOnSameLine(it) } | ||
val nonIndentLeafOnSameLineFollowingBlockComment = | ||
node | ||
.nextLeaf() | ||
?.takeIf { isNonIndentLeafOnSameLine(it) } | ||
|
||
if (nonIndentLeafOnSameLinePrecedingBlockComment != null && | ||
nonIndentLeafOnSameLineFollowingBlockComment != null | ||
) { | ||
if (nonIndentLeafOnSameLinePrecedingBlockComment.lineNumber() == nonIndentLeafOnSameLineFollowingBlockComment.lineNumber()) { | ||
// 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 | ||
) | ||
} else { | ||
// Do not try to fix constructs like below: | ||
// val foo = "foo" /* | ||
// some comment | ||
// */ val bar = "bar" | ||
emit( | ||
node.startOffset, | ||
"A block comment starting on same line as another element and ending on another line before another element is disallowed", | ||
false | ||
) | ||
} | ||
return | ||
} | ||
|
||
nonIndentLeafOnSameLinePrecedingBlockComment | ||
?.precedesBlockCommentOnSameLine(node, emit, autoCorrect) | ||
|
||
nonIndentLeafOnSameLineFollowingBlockComment | ||
?.followsBlockCommentOnSameLine(node, emit, autoCorrect) | ||
} | ||
} | ||
|
||
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 | ||
) | ||
} | ||
} | ||
|
||
private fun ASTNode.replaceWithEndOfLineComment() { | ||
val content = text.removeSurrounding("/*", "*/").trim() | ||
val eolComment = PsiCommentImpl(EOL_COMMENT, "// $content") | ||
(this as LeafPsiElement).rawInsertBeforeMe(eolComment) | ||
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) { | ||
if (elementType == WHITE_SPACE) { | ||
(this as LeafPsiElement).rawReplaceWithText("\n${blockCommentNode.lineIndent()}") | ||
} else { | ||
(this as LeafPsiElement).upsertWhitespaceBeforeMe("\n${blockCommentNode.lineIndent()}") | ||
} | ||
} | ||
} | ||
|
||
private fun isNonIndentLeafOnSameLine(it: ASTNode) = | ||
it.elementType != WHITE_SPACE || !it.textContains('\n') | ||
|
||
private fun ASTNode?.isLastElementOnLine() = | ||
this == null || (elementType == WHITE_SPACE && textContains('\n')) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
...xperimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/KdocWrappingRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package com.pinterest.ktlint.ruleset.experimental | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties | ||
import com.pinterest.ktlint.core.api.FeatureInAlphaState | ||
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties | ||
import com.pinterest.ktlint.core.ast.ElementType.KDOC | ||
import com.pinterest.ktlint.core.ast.ElementType.KDOC_END | ||
import com.pinterest.ktlint.core.ast.ElementType.KDOC_START | ||
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE | ||
import com.pinterest.ktlint.core.ast.lineIndent | ||
import com.pinterest.ktlint.core.ast.lineNumber | ||
import com.pinterest.ktlint.core.ast.nextLeaf | ||
import com.pinterest.ktlint.core.ast.prevLeaf | ||
import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement | ||
|
||
/** | ||
* Checks external wrapping of KDoc comment. Wrapping inside the KDoc comment is not altered. | ||
*/ | ||
@OptIn(FeatureInAlphaState::class) | ||
public class KdocWrappingRule : | ||
Rule("kdoc-wrapping"), | ||
UsesEditorConfigProperties { | ||
override val editorConfigProperties: List<UsesEditorConfigProperties.EditorConfigProperty<*>> = | ||
listOf( | ||
DefaultEditorConfigProperties.indentSizeProperty, | ||
DefaultEditorConfigProperties.indentStyleProperty | ||
) | ||
|
||
override fun visit( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.elementType == KDOC) { | ||
val nonIndentLeafOnSameLinePrecedingKdocComment = | ||
node | ||
.findChildByType(KDOC_START) | ||
?.prevLeaf() | ||
?.takeIf { isNonIndentLeafOnSameLine(it) } | ||
val nonIndentLeafOnSameLineFollowingKdocComment = | ||
node | ||
.findChildByType(KDOC_END) | ||
?.nextLeaf() | ||
?.takeIf { isNonIndentLeafOnSameLine(it) } | ||
|
||
if (nonIndentLeafOnSameLinePrecedingKdocComment != null && | ||
nonIndentLeafOnSameLineFollowingKdocComment != null | ||
) { | ||
if (nonIndentLeafOnSameLinePrecedingKdocComment.lineNumber() == nonIndentLeafOnSameLineFollowingKdocComment.lineNumber()) { | ||
// Do not try to fix constructs like below: | ||
// val foo /** some comment */ = "foo" | ||
emit( | ||
node.startOffset, | ||
"A KDoc comment in between other elements on the same line is disallowed", | ||
false | ||
) | ||
} else { | ||
// Do not try to fix constructs like below: | ||
// val foo = "foo" /* | ||
// some comment* | ||
// */ val bar = "bar" | ||
emit( | ||
node.startOffset, | ||
"A KDoc comment starting on same line as another element and ending on another line before another element is disallowed", | ||
false | ||
) | ||
} | ||
return | ||
} | ||
|
||
if (nonIndentLeafOnSameLinePrecedingKdocComment != null) { | ||
// It can not be autocorrected as it might depend on the situation and code style what is | ||
// preferred. | ||
emit( | ||
node.startOffset, | ||
"A KDoc comment after any other element on the same line must be separated by a new line", | ||
false | ||
) | ||
} | ||
|
||
nonIndentLeafOnSameLineFollowingKdocComment | ||
?.followsKdocCommentOnSameLine(node, emit, autoCorrect) | ||
} | ||
} | ||
|
||
private fun ASTNode.followsKdocCommentOnSameLine( | ||
kdocCommentNode: ASTNode, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, | ||
autoCorrect: Boolean | ||
) { | ||
emit(startOffset, "A KDoc comment may not be followed by any other element on that same line", true) | ||
if (autoCorrect) { | ||
if (elementType == WHITE_SPACE) { | ||
(this as LeafPsiElement).rawReplaceWithText("\n${kdocCommentNode.lineIndent()}") | ||
} else { | ||
(this as LeafPsiElement).upsertWhitespaceBeforeMe("\n${kdocCommentNode.lineIndent()}") | ||
} | ||
} | ||
} | ||
|
||
private fun isNonIndentLeafOnSameLine(it: ASTNode) = | ||
it.elementType != WHITE_SPACE || !it.textContains('\n') | ||
} |
Oops, something went wrong.