Skip to content

Commit

Permalink
Upgrade ktlint to 0.47.1
Browse files Browse the repository at this point in the history
  • Loading branch information
chao2zhang committed Sep 18, 2022
1 parent f5be0d8 commit a16d9c7
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package io.gitlab.arturbosch.detekt.formatting

import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.Rule.VisitorModifier.RunAsLateAsPossible
import com.pinterest.ktlint.core.Rule.VisitorModifier.RunOnRootNodeOnly
import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.codeStyleSetProperty
import com.pinterest.ktlint.core.api.EditorConfigProperties
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import io.github.detekt.psi.fileName
import io.github.detekt.psi.toFilePath
Expand All @@ -20,7 +20,8 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation
import io.gitlab.arturbosch.detekt.api.TextLocation
import org.ec4j.core.model.Property
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.lang.FileASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.JavaDummyElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.JavaDummyHolder
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.endOffset

Expand All @@ -38,9 +39,6 @@ abstract class FormattingRule(config: Config) : Rule(config) {
protected val isAndroid
get() = FormattingProvider.android.value(ruleSetConfig)

val runOnRootNodeOnly
get() = RunOnRootNodeOnly in wrapping.visitorModifiers

val runAsLateAsPossible
get() = RunAsLateAsPossible in wrapping.visitorModifiers

Expand All @@ -54,61 +52,87 @@ abstract class FormattingRule(config: Config) : Rule(config) {
this.root = root
positionByOffset = KtLintLineColCalculator
.calculateLineColByOffset(KtLintLineColCalculator.normalizeText(root.text))
root.node.putUserData(KtLint.FILE_PATH_USER_DATA_KEY, root.name)

wrapping.beforeFirstNode(computeEditorConfigProperties())
root.node.visitASTNodes()
wrapping.afterLastNode()
}

open fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String>? = null

val editorConfigProperties = overrideEditorConfigProperties()?.toMutableMap()
open fun getTextLocationForViolation(node: ASTNode, offset: Int) =
TextLocation(node.startOffset, node.psi.endOffset)

private fun computeEditorConfigProperties(): EditorConfigProperties {
val usesEditorConfigProperties = overrideEditorConfigProperties()?.toMutableMap()
?: mutableMapOf()

if (isAndroid) {
editorConfigProperties[codeStyleSetProperty] = "android"
usesEditorConfigProperties[codeStyleSetProperty] = "android"
}

if (editorConfigProperties.isNotEmpty()) {
val userData = (root.node.getUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY).orEmpty())
.toMutableMap()

editorConfigProperties.forEach { (editorConfigProperty, defaultValue) ->
userData[editorConfigProperty.type.name] = Property.builder()
.name(editorConfigProperty.type.name)
.type(editorConfigProperty.type)
.value(defaultValue)
.build()
return buildMap {
usesEditorConfigProperties.forEach { (editorConfigProperty, defaultValue) ->
put(
key = editorConfigProperty.type.name,
value = Property.builder()
.name(editorConfigProperty.type.name)
.type(editorConfigProperty.type)
.value(defaultValue)
.build()
)
}
root.node.putUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY, userData)
}
root.node.putUserData(KtLint.FILE_PATH_USER_DATA_KEY, root.name)
}

open fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String>? = null

fun apply(node: ASTNode) {
if (ruleShouldOnlyRunOnFileNode(node)) {
return
private fun beforeVisitChildNodes(node: ASTNode) {
wrapping.beforeVisitChildNodes(node, autoCorrect) {
offset, errorMessage, canBeAutoCorrected -> emit.invoke(offset, errorMessage, canBeAutoCorrected)
}
}

wrapping.visit(node, autoCorrect) { offset, message, _ ->
val (line, column) = positionByOffset(offset)
val location = Location(
SourceLocation(line, column),
getTextLocationForViolation(node, offset),
root.toFilePath()
)

// Nodes reported by 'NoConsecutiveBlankLines' are dangling whitespace nodes which means they have
// no direct parent which we can use to get the containing file needed to baseline or suppress findings.
// For these reasons we do not report a KtElement which may lead to crashes when postprocessing it
// e.g. reports (html), baseline etc.
val packageName = root.packageFqName.asString()
.takeIf { it.isNotEmpty() }
?.plus(".")
.orEmpty()
val entity = Entity("", "$packageName${root.fileName}:$line", location, root)
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect))
private fun afterVisitChildNodes(node: ASTNode) {
wrapping.afterVisitChildNodes(node, autoCorrect) {
offset, errorMessage, canBeAutoCorrected -> emit.invoke(offset, errorMessage, canBeAutoCorrected)
}
}

open fun getTextLocationForViolation(node: ASTNode, offset: Int) =
TextLocation(node.startOffset, node.psi.endOffset)
private val emit = { offset: Int, message: String, _: Boolean ->
val (line, column) = positionByOffset(offset)
val location = Location(
SourceLocation(line, column),
// Use offset + 1 since ktlint always reports a single location.
TextLocation(offset, offset + 1),
root.toFilePath()
)

// Nodes reported by 'NoConsecutiveBlankLines' are dangling whitespace nodes which means they have
// no direct parent which we can use to get the containing file needed to baseline or suppress findings.
// For these reasons we do not report a KtElement which may lead to crashes when postprocessing it
// e.g. reports (html), baseline etc.
val packageName = root.packageFqName.asString()
.takeIf { it.isNotEmpty() }
?.plus(".")
.orEmpty()
val entity = Entity("", "$packageName${root.fileName}:$line", location, root)
report(CorrectableCodeSmell(issue, entity, message, autoCorrectEnabled = autoCorrect))
}

private fun ruleShouldOnlyRunOnFileNode(node: ASTNode) =
runOnRootNodeOnly && node !is FileASTNode
private fun ASTNode.visitASTNodes() {
if (isNotDummyElement()) {
beforeVisitChildNodes(this)
}
getChildren(null).forEach {
it.visitASTNodes()
}
if (isNotDummyElement()) {
afterVisitChildNodes(this)
}
}

private fun ASTNode.isNotDummyElement(): Boolean {
val parent = this.psi?.parent
return parent !is JavaDummyHolder && parent !is JavaDummyElement
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenDeclaration
import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenDeclarationsWithComments
import io.gitlab.arturbosch.detekt.formatting.wrappers.SpacingBetweenFunctionNameAndOpeningParenthesis
import io.gitlab.arturbosch.detekt.formatting.wrappers.StringTemplate
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingComma
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingCommaOnCallSite
import io.gitlab.arturbosch.detekt.formatting.wrappers.TrailingCommaOnDeclarationSite
import io.gitlab.arturbosch.detekt.formatting.wrappers.TypeArgumentListSpacing
import io.gitlab.arturbosch.detekt.formatting.wrappers.TypeParameterListSpacing
import io.gitlab.arturbosch.detekt.formatting.wrappers.UnnecessaryParenthesesBeforeTrailingLambda
Expand Down Expand Up @@ -119,7 +120,8 @@ class KtLintMultiRule(config: Config = Config.empty) :
SpacingBetweenDeclarationsWithAnnotations(config),
SpacingBetweenDeclarationsWithComments(config),
StringTemplate(config),
TrailingComma(config), // in standard ruleset but not enabled by default
TrailingCommaOnCallSite(config), // in standard ruleset but not enabled by default
TrailingCommaOnDeclarationSite(config), // in standard ruleset but not enabled by default
Wrapping(config),

// Wrappers for ktlint-ruleset-experimental rules. Disabled by default.
Expand All @@ -142,43 +144,23 @@ class KtLintMultiRule(config: Config = Config.empty) :
)

override fun visit(root: KtFile) {
val sortedRules = getSortedRules()
sortedRules.forEach { it.visit(root) }
root.node.visitTokens { node ->
sortedRules.forEach { it.apply(node) }
getSortedRules().forEach { rule ->
rule.visit(root)
}
}

internal fun getSortedRules(): List<FormattingRule> {
val runFirstOnRoot = mutableListOf<FormattingRule>()
val other = mutableListOf<FormattingRule>()
val runLastOnRoot = mutableListOf<FormattingRule>()
val runLast = mutableListOf<FormattingRule>()
for (rule in activeRules.filterIsInstance<FormattingRule>()) {
when {
rule.runOnRootNodeOnly && rule.runAsLateAsPossible -> runLastOnRoot.add(rule)
rule.runOnRootNodeOnly -> runFirstOnRoot.add(rule)
rule.runAsLateAsPossible -> runLast.add(rule)
else -> other.add(rule)
}
}
return LinkedList<FormattingRule>().apply {
addAll(runFirstOnRoot)
addAll(other)
addAll(runLastOnRoot)
addAll(runLast)
}
}

private fun ASTNode.visitTokens(currentNode: (ASTNode) -> Unit) {
if (this.isNoFakeElement()) {
currentNode(this)
}
getChildren(null).forEach { it.visitTokens(currentNode) }
}

private fun ASTNode.isNoFakeElement(): Boolean {
val parent = this.psi?.parent
return parent !is JavaDummyHolder && parent !is JavaDummyElement
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,4 @@ class Indentation(config: Config) : FormattingRule(config) {
mapOf(
DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(),
)

/**
* [IndentationRule] has visitor modifier RunOnRootNodeOnly, so [node] is always the root file.
* Override the parent implementation to highlight the entire file.
*/
override fun getTextLocationForViolation(node: ASTNode, offset: Int): TextLocation {
val relativeEnd = node.text
.drop(offset)
.indexOfFirst { !it.isWhitespace() }
return TextLocation(offset, offset + relativeEnd)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.gitlab.arturbosch.detekt.formatting.wrappers

import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.standard.TrailingCommaOnCallSiteRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.formatting.FormattingRule

/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation.
*/
@AutoCorrectable(since = "1.23.0")
class TrailingCommaOnCallSite(config: Config) : FormattingRule(config) {

override val wrapping = TrailingCommaOnCallSiteRule()
override val issue = issueFor("Rule to mandate/forbid trailing commas")

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the call side")
private val allowTrailingComma by config(false)

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
TrailingCommaOnCallSiteRule.allowTrailingCommaOnCallSiteProperty to allowTrailingComma.toString(),
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.gitlab.arturbosch.detekt.formatting.wrappers

import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule
import com.pinterest.ktlint.ruleset.standard.TrailingCommaOnDeclarationSiteRule
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.AutoCorrectable
Expand All @@ -11,21 +11,17 @@ import io.gitlab.arturbosch.detekt.formatting.FormattingRule
/**
* See [ktlint docs](https://pinterest.github.io/ktlint/rules/standard/) for documentation.
*/
@AutoCorrectable(since = "1.20.0")
class TrailingComma(config: Config) : FormattingRule(config) {
@AutoCorrectable(since = "1.23.0")
class TrailingCommaOnDeclarationSite(config: Config) : FormattingRule(config) {

override val wrapping = TrailingCommaRule()
override val wrapping = TrailingCommaOnDeclarationSiteRule()
override val issue = issueFor("Rule to mandate/forbid trailing commas")

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the defining side")
@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the declaration side")
private val allowTrailingComma by config(false)

@Configuration("Defines whether a trailing comma (or no trailing comma) should be enforced on the calling side")
private val allowTrailingCommaOnCallSite by config(false)

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
TrailingCommaRule.allowTrailingCommaProperty to allowTrailingComma.toString(),
TrailingCommaRule.allowTrailingCommaOnCallSiteProperty to allowTrailingCommaOnCallSite.toString(),
TrailingCommaOnDeclarationSiteRule.allowTrailingCommaProperty to allowTrailingComma.toString(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ class Wrapping(config: Config) : FormattingRule(config) {
@Configuration("indentation size")
private val indentSize by config(4)

/**
* [Wrapping] has visitor modifier RunOnRootNodeOnly, so [node] is always the root file.
* Override the parent implementation to highlight the entire file.
*/
override fun getTextLocationForViolation(node: ASTNode, offset: Int): TextLocation {
// Use offset + 1 since Wrapping always reports the location of missing new line.
return TextLocation(offset, offset + 1)
}

override fun overrideEditorConfigProperties(): Map<UsesEditorConfigProperties.EditorConfigProperty<*>, String> =
mapOf(
DefaultEditorConfigProperties.indentSizeProperty to indentSize.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,8 @@ class KtLintMultiRuleSpec {
ktlintRule.visitFile(compileContentForTest(""))
val sortedRules = ktlintRule.getSortedRules()
assertThat(sortedRules).isNotEmpty
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly })
assertThat(sortedRules.indexOfFirst { !it.runAsLateAsPossible })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
assertThat(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
.isGreaterThan(-1)
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible && !it.runOnRootNodeOnly })
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import java.nio.file.Paths
fun FormattingRule.lint(@Language("kotlin") content: String, fileName: String = "Test.kt"): List<Finding> {
val root = compileContentForTest(content, fileName)
this.visit(root)
root.node.visit { node -> this.apply(node) }
return this.findings
}

Expand Down
Loading

0 comments on commit a16d9c7

Please sign in to comment.