-
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.
- Loading branch information
1 parent
e379b33
commit 6ff38b1
Showing
10 changed files
with
1,498 additions
and
1,325 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
160 changes: 160 additions & 0 deletions
160
...dard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnCallSiteRule.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,160 @@ | ||
package com.pinterest.ktlint.ruleset.standard | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.api.EditorConfigProperties | ||
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties | ||
import com.pinterest.ktlint.core.ast.ElementType | ||
import com.pinterest.ktlint.core.ast.children | ||
import com.pinterest.ktlint.ruleset.standard.internal.trailingcomma.reportAndCorrectTrailingCommaNodeBefore | ||
import kotlin.properties.Delegates | ||
import org.ec4j.core.model.PropertyType | ||
import org.ec4j.core.model.PropertyType.PropertyValueParser | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet | ||
|
||
/** | ||
* Linting trailing comma for call site. | ||
* | ||
* @see [Kotlin Style Guide](https://kotlinlang.org/docs/coding-conventions.html#trailing-commas) | ||
*/ | ||
public class TrailingCommaOnCallSiteRule : | ||
Rule( | ||
id = "trailing-comma-on-call-site", | ||
visitorModifiers = setOf( | ||
VisitorModifier.RunAfterRule( | ||
ruleId = "standard:indent", | ||
loadOnlyWhenOtherRuleIsLoaded = true, | ||
runOnlyWhenOtherRuleIsEnabled = true | ||
), | ||
VisitorModifier.RunAsLateAsPossible | ||
) | ||
), | ||
UsesEditorConfigProperties { | ||
|
||
override val editorConfigProperties: List<UsesEditorConfigProperties.EditorConfigProperty<*>> = listOf( | ||
allowTrailingCommaOnCallSiteProperty | ||
) | ||
|
||
private var allowTrailingCommaOnCallSite by Delegates.notNull<Boolean>() | ||
|
||
private fun ASTNode.isTrailingCommaAllowed() = | ||
elementType in TYPES_ON_CALL_SITE && allowTrailingCommaOnCallSite | ||
|
||
override fun beforeFirstNode(editorConfigProperties: EditorConfigProperties) { | ||
allowTrailingCommaOnCallSite = editorConfigProperties.getEditorConfigValue(allowTrailingCommaOnCallSiteProperty) | ||
} | ||
|
||
override fun beforeVisitChildNodes( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
// Keep processing of element types in sync with Intellij Kotlin formatting settings. | ||
// https://github.com/JetBrains/intellij-kotlin/blob/master/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt | ||
when (node.elementType) { | ||
ElementType.COLLECTION_LITERAL_EXPRESSION -> visitCollectionLiteralExpression(node, autoCorrect, emit) | ||
ElementType.INDICES -> visitIndices(node, autoCorrect, emit) | ||
ElementType.TYPE_ARGUMENT_LIST -> visitTypeList(node, autoCorrect, emit) | ||
ElementType.VALUE_ARGUMENT_LIST -> visitValueList(node, autoCorrect, emit) | ||
else -> Unit | ||
} | ||
} | ||
|
||
private fun visitCollectionLiteralExpression( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.last { it.elementType == ElementType.RBRACKET } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
emit = emit, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect | ||
) | ||
} | ||
|
||
private fun visitIndices( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.last { it.elementType == ElementType.RBRACKET } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
emit = emit, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect | ||
) | ||
} | ||
|
||
private fun visitValueList( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.treeParent.elementType != ElementType.FUNCTION_LITERAL) { | ||
node | ||
.children() | ||
.lastOrNull { it.elementType == ElementType.RPAR } | ||
?.let { inspectNode -> | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
emit = emit, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun visitTypeList( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.first { it.elementType == ElementType.GT } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
emit = emit, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect | ||
) | ||
} | ||
|
||
public companion object { | ||
|
||
internal const val ALLOW_TRAILING_COMMA_ON_CALL_SITE_NAME = "ij_kotlin_allow_trailing_comma_on_call_site" | ||
private const val ALLOW_TRAILING_COMMA_ON_CALL_SITE_DESCRIPTION = | ||
"Defines whether a trailing comma (or no trailing comma)" + | ||
"should be enforced on the calling side," + | ||
"e.g. argument-list, when-entries, lambda-arguments, indices, etc." | ||
private val BOOLEAN_VALUES_SET = setOf("true", "false") | ||
|
||
// TODO: Rename property to trailingCommaOnCallSite. The word 'allow' is misleading as the comma is | ||
// enforced when the property is enabled and prohibited when disabled. | ||
public val allowTrailingCommaOnCallSiteProperty: UsesEditorConfigProperties.EditorConfigProperty<Boolean> = | ||
UsesEditorConfigProperties.EditorConfigProperty( | ||
type = PropertyType.LowerCasingPropertyType( | ||
ALLOW_TRAILING_COMMA_ON_CALL_SITE_NAME, | ||
ALLOW_TRAILING_COMMA_ON_CALL_SITE_DESCRIPTION, | ||
PropertyValueParser.BOOLEAN_VALUE_PARSER, | ||
BOOLEAN_VALUES_SET | ||
), | ||
defaultValue = false | ||
) | ||
|
||
private val TYPES_ON_CALL_SITE = TokenSet.create( | ||
ElementType.COLLECTION_LITERAL_EXPRESSION, | ||
ElementType.INDICES, | ||
ElementType.TYPE_ARGUMENT_LIST, | ||
ElementType.VALUE_ARGUMENT_LIST | ||
) | ||
} | ||
} |
191 changes: 191 additions & 0 deletions
191
...c/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnDeclarationSiteRule.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,191 @@ | ||
package com.pinterest.ktlint.ruleset.standard | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.api.EditorConfigProperties | ||
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties | ||
import com.pinterest.ktlint.core.ast.ElementType | ||
import com.pinterest.ktlint.core.ast.children | ||
import com.pinterest.ktlint.ruleset.standard.internal.trailingcomma.reportAndCorrectTrailingCommaNodeBefore | ||
import kotlin.properties.Delegates | ||
import org.ec4j.core.model.PropertyType | ||
import org.ec4j.core.model.PropertyType.PropertyValueParser | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet | ||
import org.jetbrains.kotlin.psi.KtWhenEntry | ||
import org.jetbrains.kotlin.psi.KtWhenExpression | ||
import org.jetbrains.kotlin.utils.addToStdlib.cast | ||
|
||
/** | ||
* Linting trailing comma for declaration site. | ||
* | ||
* @see [Kotlin Style Guide](https://kotlinlang.org/docs/coding-conventions.html#trailing-commas) | ||
*/ | ||
public class TrailingCommaOnDeclarationSiteRule : | ||
Rule( | ||
id = "trailing-comma-on-declaration-site", | ||
visitorModifiers = setOf( | ||
VisitorModifier.RunAfterRule( | ||
ruleId = "standard:indent", | ||
loadOnlyWhenOtherRuleIsLoaded = true, | ||
runOnlyWhenOtherRuleIsEnabled = true | ||
), | ||
VisitorModifier.RunAsLateAsPossible | ||
) | ||
), | ||
UsesEditorConfigProperties { | ||
|
||
override val editorConfigProperties: List<UsesEditorConfigProperties.EditorConfigProperty<*>> = listOf( | ||
allowTrailingCommaProperty | ||
) | ||
|
||
private var allowTrailingComma by Delegates.notNull<Boolean>() | ||
|
||
private fun ASTNode.isTrailingCommaAllowed() = | ||
elementType in TYPES_ON_DECLARATION_SITE && allowTrailingComma | ||
|
||
override fun beforeFirstNode(editorConfigProperties: EditorConfigProperties) { | ||
allowTrailingComma = editorConfigProperties.getEditorConfigValue(allowTrailingCommaProperty) | ||
} | ||
|
||
override fun beforeVisitChildNodes( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
// Keep processing of element types in sync with Intellij Kotlin formatting settings. | ||
// https://github.com/JetBrains/intellij-kotlin/blob/master/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt | ||
when (node.elementType) { | ||
ElementType.DESTRUCTURING_DECLARATION -> visitDestructuringDeclaration(node, autoCorrect, emit) | ||
ElementType.FUNCTION_LITERAL -> visitFunctionLiteral(node, autoCorrect, emit) | ||
ElementType.TYPE_PARAMETER_LIST -> visitTypeList(node, autoCorrect, emit) | ||
ElementType.VALUE_PARAMETER_LIST -> visitValueList(node, autoCorrect, emit) | ||
ElementType.WHEN_ENTRY -> visitWhenEntry(node, autoCorrect, emit) | ||
else -> Unit | ||
} | ||
} | ||
|
||
private fun visitDestructuringDeclaration( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.last { it.elementType == ElementType.RPAR } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect, | ||
emit = emit | ||
) | ||
} | ||
|
||
private fun visitFunctionLiteral( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.lastOrNull { it.elementType == ElementType.ARROW } | ||
?: // lambda w/o an arrow -> no arguments -> no commas | ||
return | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect, | ||
emit = emit | ||
) | ||
} | ||
|
||
private fun visitValueList( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
if (node.treeParent.elementType != ElementType.FUNCTION_LITERAL) { | ||
node | ||
.children() | ||
.lastOrNull { it.elementType == ElementType.RPAR } | ||
?.let { inspectNode -> | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect, | ||
emit = emit | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun visitTypeList( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val inspectNode = node | ||
.children() | ||
.first { it.elementType == ElementType.GT } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect, | ||
emit = emit | ||
) | ||
} | ||
|
||
private fun visitWhenEntry( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit | ||
) { | ||
val psi = node.psi | ||
require(psi is KtWhenEntry) | ||
if (psi.isElse || psi.parent.cast<KtWhenExpression>().leftParenthesis == null) { | ||
// no commas for "else" or when there are no opening parenthesis for the when-expression | ||
return | ||
} | ||
|
||
val inspectNode = node | ||
.children() | ||
.first { it.elementType == ElementType.ARROW } | ||
node.reportAndCorrectTrailingCommaNodeBefore( | ||
inspectNode = inspectNode, | ||
isTrailingCommaAllowed = node.isTrailingCommaAllowed(), | ||
autoCorrect = autoCorrect, | ||
emit = emit | ||
) | ||
} | ||
|
||
public companion object { | ||
|
||
internal const val ALLOW_TRAILING_COMMA_NAME = "ij_kotlin_allow_trailing_comma" | ||
private const val ALLOW_TRAILING_COMMA_DESCRIPTION = "Defines whether a trailing comma (or no trailing comma)" + | ||
"should be enforced on the defining side," + | ||
"e.g. parameter-list, type-argument-list, lambda-value-parameters, enum-entries, etc." | ||
|
||
private val BOOLEAN_VALUES_SET = setOf("true", "false") | ||
|
||
// TODO: Rename property to trailingCommaOnDeclarationSite. The word 'allow' is misleading as the comma is | ||
// enforced when the property is enabled and prohibited when disabled. | ||
public val allowTrailingCommaProperty: UsesEditorConfigProperties.EditorConfigProperty<Boolean> = | ||
UsesEditorConfigProperties.EditorConfigProperty( | ||
type = PropertyType.LowerCasingPropertyType( | ||
ALLOW_TRAILING_COMMA_NAME, | ||
ALLOW_TRAILING_COMMA_DESCRIPTION, | ||
PropertyValueParser.BOOLEAN_VALUE_PARSER, | ||
BOOLEAN_VALUES_SET | ||
), | ||
defaultValue = false | ||
) | ||
|
||
private val TYPES_ON_DECLARATION_SITE = TokenSet.create( | ||
ElementType.DESTRUCTURING_DECLARATION, | ||
ElementType.FUNCTION_LITERAL, | ||
ElementType.FUNCTION_TYPE, | ||
ElementType.TYPE_PARAMETER_LIST, | ||
ElementType.VALUE_PARAMETER_LIST, | ||
ElementType.WHEN_ENTRY | ||
) | ||
} | ||
} |
Oops, something went wrong.