Skip to content

Commit

Permalink
Split TrailingCommaRule
Browse files Browse the repository at this point in the history
  • Loading branch information
chao2zhang committed Jul 24, 2022
1 parent e379b33 commit 6ff38b1
Show file tree
Hide file tree
Showing 10 changed files with 1,498 additions and 1,325 deletions.
3 changes: 0 additions & 3 deletions .editorconfig
Expand Up @@ -15,9 +15,6 @@ indent_size = 4

[*.{kt,kts}]
ij_kotlin_imports_layout=*
# Ideally, no experimental rule should be disabled. Ktlint should follow the dogfooding principle. This means that an
# experimental rule should only be added to the master branch no sooner than that this rule has been applied on the
# ktlint code base itself.
ij_kotlin_allow_trailing_comma=false
ij_kotlin_allow_trailing_comma_on_call_site=false

Expand Down
Expand Up @@ -2,7 +2,6 @@ package com.pinterest.ktlint.ruleset.standard

import com.pinterest.ktlint.core.RuleSet
import com.pinterest.ktlint.core.RuleSetProvider
import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule

public class StandardRuleSetProvider : RuleSetProvider {

Expand Down Expand Up @@ -51,7 +50,8 @@ public class StandardRuleSetProvider : RuleSetProvider {
SpacingBetweenDeclarationsWithAnnotationsRule(),
SpacingBetweenDeclarationsWithCommentsRule(),
StringTemplateRule(),
TrailingCommaRule(),
TrailingCommaOnDeclarationSiteRule(),
TrailingCommaOnCallSiteRule(),
WrappingRule()
)
}
@@ -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
)
}
}
@@ -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
)
}
}

0 comments on commit 6ff38b1

Please sign in to comment.