-
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.
Merge pull request #1846 from pinterest/1775-wrapping-parameter-type
Wrapping value parameter and property
- Loading branch information
Showing
12 changed files
with
766 additions
and
31 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
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
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
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
181 changes: 181 additions & 0 deletions
181
...dard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterWrappingRule.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,181 @@ | ||
package com.pinterest.ktlint.ruleset.standard.rules | ||
|
||
import com.pinterest.ktlint.logger.api.initKtLintKLogger | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.COMMA | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE | ||
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER | ||
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig | ||
import com.pinterest.ktlint.rule.engine.core.api.RuleId | ||
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig | ||
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.editorconfig.MAX_LINE_LENGTH_PROPERTY | ||
import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf | ||
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.leavesIncludingSelf | ||
import com.pinterest.ktlint.rule.engine.core.api.lineIndent | ||
import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf | ||
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf | ||
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe | ||
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe | ||
import com.pinterest.ktlint.ruleset.standard.StandardRule | ||
import mu.KotlinLogging | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
|
||
private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger() | ||
|
||
/** | ||
* This rule inserts missing newlines inside a value parameter. | ||
* | ||
* Whenever a linebreak is inserted, this rules assumes that the parent node it indented correctly. So the indentation | ||
* is fixed with respect to indentation of the parent. This is just a simple best effort for the case that the | ||
* indentation rule is not run. | ||
* | ||
* This rule has many similarities with the [PropertyWrappingRule] but some subtle differences. | ||
*/ | ||
public class ParameterWrappingRule : | ||
StandardRule( | ||
id = "parameter-wrapping", | ||
usesEditorConfigProperties = setOf( | ||
INDENT_SIZE_PROPERTY, | ||
INDENT_STYLE_PROPERTY, | ||
MAX_LINE_LENGTH_PROPERTY, | ||
), | ||
) { | ||
private var line = 1 | ||
private lateinit var indentConfig: IndentConfig | ||
private var maxLineLength: Int = MAX_LINE_LENGTH_PROPERTY.defaultValue | ||
|
||
override fun beforeFirstNode(editorConfig: EditorConfig) { | ||
line = 1 | ||
indentConfig = IndentConfig( | ||
indentStyle = editorConfig[INDENT_STYLE_PROPERTY], | ||
tabWidth = editorConfig[INDENT_SIZE_PROPERTY], | ||
) | ||
maxLineLength = editorConfig[MAX_LINE_LENGTH_PROPERTY] | ||
} | ||
|
||
override fun beforeVisitChildNodes( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, | ||
) { | ||
if (node.elementType == VALUE_PARAMETER) { | ||
rearrangeValueParameter(node, autoCorrect, emit) | ||
} | ||
} | ||
|
||
private fun rearrangeValueParameter( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, | ||
) { | ||
require(node.elementType == VALUE_PARAMETER) | ||
|
||
val baseIndent = node.lineIndent() | ||
|
||
// Find the first node after the indenting whitespace on the same line as the identifier | ||
val nodeFirstChildLeafOrSelf = node.firstChildLeafOrSelf() | ||
val fromNode = | ||
node | ||
.findChildByType(ElementType.IDENTIFIER) | ||
?.leavesIncludingSelf(forward = false) | ||
?.firstOrNull { it.prevLeaf().isWhiteSpaceWithNewline() || it == nodeFirstChildLeafOrSelf } | ||
?: node | ||
|
||
node | ||
.findChildByType(COLON) | ||
?.let { colon -> | ||
if (baseIndent.length + fromNode.sumOfTextLengthUntil(colon) > maxLineLength) { | ||
fromNode.sumOfTextLengthUntil(colon) | ||
requireNewlineAfterLeaf(colon, autoCorrect, emit, baseIndent) | ||
return | ||
} | ||
} | ||
|
||
node | ||
.findChildByType(TYPE_REFERENCE) | ||
?.let { typeReference -> | ||
if (baseIndent.length + fromNode.sumOfTextLengthUntil(typeReference.orTrailingComma()) > maxLineLength) { | ||
requireNewlineBeforeLeaf(typeReference, autoCorrect, emit, baseIndent) | ||
return | ||
} | ||
} | ||
|
||
node | ||
.findChildByType(EQ) | ||
?.let { equal -> | ||
if (baseIndent.length + fromNode.sumOfTextLengthUntil(equal.orTrailingComma()) > maxLineLength) { | ||
requireNewlineAfterLeaf(equal, autoCorrect, emit, baseIndent) | ||
return | ||
} | ||
} | ||
|
||
node | ||
.findChildByType(CALL_EXPRESSION) | ||
?.let { callExpression -> | ||
if (baseIndent.length + fromNode.sumOfTextLengthUntil(callExpression.orTrailingComma()) > maxLineLength) { | ||
requireNewlineBeforeLeaf(callExpression, autoCorrect, emit, baseIndent) | ||
return | ||
} | ||
} | ||
} | ||
|
||
private fun ASTNode.orTrailingComma() = | ||
lastChildLeafOrSelf() | ||
.nextCodeLeaf() | ||
?.takeIf { it.elementType == COMMA } | ||
?: this | ||
|
||
private fun ASTNode.sumOfTextLengthUntil(astNode: ASTNode): Int { | ||
val stopAtLeaf = astNode.lastChildLeafOrSelf() | ||
return leavesIncludingSelf() | ||
.takeWhile { !it.isWhiteSpaceWithNewline() && it.prevLeaf() != stopAtLeaf } | ||
.sumOf { it.textLength } | ||
} | ||
|
||
private fun requireNewlineBeforeLeaf( | ||
node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, | ||
indent: String, | ||
) { | ||
emit( | ||
node.startOffset - 1, | ||
"""Missing newline before "${node.text}"""", | ||
true, | ||
) | ||
LOGGER.trace { "$line: " + ((if (!autoCorrect) "would have " else "") + "inserted newline before ${node.text}") } | ||
if (autoCorrect) { | ||
node.upsertWhitespaceBeforeMe("\n" + indent) | ||
} | ||
} | ||
|
||
private fun requireNewlineAfterLeaf( | ||
nodeAfterWhichNewlineIsRequired: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, | ||
indent: String? = null, | ||
nodeToFix: ASTNode = nodeAfterWhichNewlineIsRequired, | ||
) { | ||
emit( | ||
nodeAfterWhichNewlineIsRequired.startOffset + 1, | ||
"""Missing newline after "${nodeAfterWhichNewlineIsRequired.text}"""", | ||
true, | ||
) | ||
LOGGER.trace { | ||
"$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" | ||
} | ||
if (autoCorrect) { | ||
val tempIndent = indent ?: (nodeToFix.lineIndent() + indentConfig.indent) | ||
nodeToFix.upsertWhitespaceAfterMe("\n" + tempIndent) | ||
} | ||
} | ||
} | ||
|
||
public val PARAMETER_WRAPPING_RULE_ID: RuleId = ParameterWrappingRule().ruleId |
Oops, something went wrong.