Skip to content

Commit

Permalink
### Whats added:
Browse files Browse the repository at this point in the history
 * Start logic to fix and warn String Template in Linelength rule
  • Loading branch information
Arrgentum committed May 20, 2022
1 parent c00df8b commit 5e52117
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,10 @@ import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isWhiteSpace
import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline
import com.pinterest.ktlint.core.ast.nextSibling
import com.pinterest.ktlint.core.ast.parent
import com.pinterest.ktlint.core.ast.prevSibling
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.psi.parameterRecursiveVisitor
import org.jetbrains.kotlin.psi.psiUtil.parents

import java.net.MalformedURLException
import java.net.URL
Expand Down Expand Up @@ -131,18 +125,34 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
)
private fun isFixable(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases {
var parent = wrongNode
var stringOrDot : ASTNode? = null
do {
when (parent.elementType) {
BINARY_EXPRESSION, PARENTHESIZED -> {
val splitOffset = searchRightSplitAfterType(parent, configuration, OPERATION_REFERENCE)?.second
splitOffset?.let {
if (isConditionToUpAnalysisBinExpression(parent, splitOffset)) {
parent = parent.treeParent
} else {
return checkBinaryExpression(parent, configuration)
parent.findParentNodeWithSpecificType(VALUE_ARGUMENT_LIST) ?. let {
parent = it
} ?: parent.findParentNodeWithSpecificType(FUNCTION_LITERAL) ?. let {
parent = it
} ?: run {
val splitOffset = searchRightSplitAfterType(parent, configuration, OPERATION_REFERENCE)?.second
splitOffset?.let {
val parentIsBiExprOrParenthesized = parent.treeParent.elementType in listOf(BINARY_EXPRESSION, PARENTHESIZED)
val parentIsFunOrProperty = parent.treeParent.elementType in listOf(FUN, PROPERTY)
if (parentIsBiExprOrParenthesized) {
parent = parent.treeParent
} else if (parentIsFunOrProperty && splitOffset >= configuration.lineLength) {
if (stringOrDot != null) {
val returnElem = checkStringTemplateAndDotQualifiedExpression(parent, configuration)
if (returnElem !is None)
return returnElem
}
parent = parent.treeParent
} else {
return checkBinaryExpression(parent, configuration)
}
}
?: run { parent = parent.treeParent }
}
?: run { parent = parent.treeParent }
}
FUN, PROPERTY -> return checkFunAndProperty(parent)
CONDITION -> return checkCondition(parent, configuration)
Expand All @@ -154,6 +164,7 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
EOL_COMMENT -> return checkComment(parent, configuration)
FUNCTION_LITERAL -> return Lambda(parent)
STRING_TEMPLATE, DOT_QUALIFIED_EXPRESSION -> {
stringOrDot = parent
parent.findParentNodeWithSpecificType(BINARY_EXPRESSION) ?. let {
parent = it
} ?: parent.findParentNodeWithSpecificType(VALUE_ARGUMENT_LIST) ?. let {
Expand All @@ -178,9 +189,8 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
*/
private fun isConditionToUpAnalysisBinExpression(parent: ASTNode, offset: Int): Boolean {
val parentIsBiExprOrParenthesized = parent.treeParent.elementType in listOf(BINARY_EXPRESSION, PARENTHESIZED)
val parentIsFunctionLiteral = parent.treeParent.treeParent.elementType == FUNCTION_LITERAL
val parentIsFunOrProperty = parent.treeParent.elementType in listOf(FUN, PROPERTY)
return (parentIsBiExprOrParenthesized || parentIsFunctionLiteral || (parentIsFunOrProperty && offset >= configuration.lineLength))
return (parentIsBiExprOrParenthesized || (parentIsFunOrProperty && offset >= configuration.lineLength))
}

/**
Expand All @@ -196,7 +206,7 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
return LongBinaryExpression(node, configuration, leftOffset, binList)
}

private fun checkStringTemplateAndDotQualifiedExpression(node: ASTNode, configuration: LineLengthConfiguration, funOrPropertyNode : ASTNode?) : LongLineFixableCases {
private fun checkStringTemplateAndDotQualifiedExpression(node: ASTNode, configuration: LineLengthConfiguration, funOrPropertyNode : ASTNode? = null) : LongLineFixableCases {
funOrPropertyNode ?.let {
if (it.hasChildOfType(EQ)) {
val positionByOffset = positionByOffset(it.getFirstChildWithType(EQ)!!.startOffset).second
Expand Down Expand Up @@ -276,7 +286,7 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
private fun parserDotQualifiedExpression(wrongNode: ASTNode, configuration: LineLengthConfiguration) : LongLineFixableCases {
val nodeDot = searchRightSplitAfterType(wrongNode, configuration, DOT)?.first
nodeDot ?. let {
return DotQualifiedExpression(wrongNode, it)
return DotQualifiedExpression(wrongNode)
} ?:
return None()
}
Expand Down Expand Up @@ -345,17 +355,18 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(

private fun fixDotQualifiedExpression(wrongDotQualifiedExpression: DotQualifiedExpression) {
val node = wrongDotQualifiedExpression.node
val dot = wrongDotQualifiedExpression.nodeForSplit
val dot = node.getFirstChildWithType(DOT)
node.appendNewlineMergingWhiteSpace(dot,dot)
}
private fun fixArgumentList(wrongArgumentList: ValueArgumentList) {
val node = wrongArgumentList.node
val comma = wrongArgumentList.nodeForSplit
val nodeWithComma = wrongArgumentList.nodeForSplit
val comma = nodeWithComma?.getFirstChildWithType(COMMA)
comma ?. let {
node.appendNewlineMergingWhiteSpace(comma, comma)
nodeWithComma.appendNewlineMergingWhiteSpace(comma, comma)
} ?: run {
node.appendNewlineMergingWhiteSpace(node.findChildByType(LPAR)!!.treeNext, node.findChildByType(LPAR)!!.treeNext)
node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR)!!.treePrev, node.findChildByType(RPAR)!!.treePrev)
node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR), node.findChildByType(RPAR))
node.getChildren(null).forEach { elem->
if (elem.elementType == COMMA && elem.treeNext.elementType != RPAR)
node.appendNewlineMergingWhiteSpace(elem.treeNext, elem.treeNext)
Expand Down Expand Up @@ -426,13 +437,13 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
val incorrectText = wrongStringTemplate.node.text
val firstPart = incorrectText.substring(0, wrongStringTemplate.delimiterIndex)
val secondPart = incorrectText.substring(wrongStringTemplate.delimiterIndex, incorrectText.length)
val textBetwenParts =
val textBetweenParts =
if (wrongStringTemplate.isOneLineString) {
"\" +\n\""
} else {
"\n"
}
val correctNode = KotlinParser().createNode("$firstPart$textBetwenParts$secondPart")
val correctNode = KotlinParser().createNode("$firstPart$textBetweenParts$secondPart")
wrongStringTemplate.node.treeParent.replaceChild(wrongStringTemplate.node, correctNode)
}

Expand Down Expand Up @@ -485,6 +496,32 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
}
}

private fun searchComma(node: ASTNode, commaList: MutableList<ASTNode>) {
if (node.elementType == VALUE_ARGUMENT_LIST){
node.getChildren(null)
.filter {
it.elementType == VALUE_ARGUMENT_LIST
}
.forEach {
searchComma(it, commaList)
}
commaList.add(node)
}
}

private fun searchDot(node: ASTNode, DotList: MutableList<ASTNode>) {
if (node.elementType == DOT_QUALIFIED_EXPRESSION){
node.getChildren(null)
.filter {
it.elementType == DOT_QUALIFIED_EXPRESSION
}
.forEach {
searchDot(it, DotList)
}
DotList.add(node)
}
}

/**
* This method stored all the nodes that have BINARY_EXPRESSION or PREFIX_EXPRESSION element type.
* Return List of the Pair <node, offset>
Expand Down Expand Up @@ -538,15 +575,19 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
*/
@Suppress("UnsafeCallOnNullableType")
private fun searchRightSplitAfterType(parent: ASTNode, configuration: LineLengthConfiguration, type: IElementType): Pair<ASTNode, Int>? {
val binList: MutableList<ASTNode> = mutableListOf()
searchBinaryExpression(parent, binList)
return binList.map {
it to positionByOffset(it.getFirstChildWithType(type)!!.startOffset).second
val list: MutableList<ASTNode> = mutableListOf()
when (type) {
OPERATION_REFERENCE -> searchBinaryExpression(parent, list)
COMMA -> searchComma(parent, list)
DOT -> searchDot(parent, list)
}
return list.map {
it to positionByOffset(it.getFirstChildWithType(type)?.startOffset ?: configuration.lineLength.toInt()).second
}
.sortedBy { it.second }
.reversed()
.firstOrNull { (it, offset) ->
offset + (it.getFirstChildWithType(type)?.text!!.length ?: 0) <= configuration.lineLength + 1
offset + (it.getFirstChildWithType(type)?.text?.length ?: 0) <= configuration.lineLength + 1
}
}

Expand Down Expand Up @@ -637,9 +678,9 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
private class Lambda(node: ASTNode) : LongLineFixableCases(node)

/**
* Class DotQualifiedExpression show that line should be split in DotQualifiedExpression after node [nodeForSplit]
* Class DotQualifiedExpression show that line should be split in DotQualifiedExpression
*/
private class DotQualifiedExpression(node: ASTNode, val nodeForSplit: ASTNode) : LongLineFixableCases(node)
private class DotQualifiedExpression(node: ASTNode) : LongLineFixableCases(node)

/**
* Class ValueArgumentList show that line should be split in ValueArgumentList:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,9 @@ fun ASTNode.leaveExactlyNumNewLines(num: Int) {
*/
fun ASTNode.appendNewlineMergingWhiteSpace(whiteSpaceNode: ASTNode?, beforeNode: ASTNode?) {
if (whiteSpaceNode != null && whiteSpaceNode.elementType == WHITE_SPACE) {
(whiteSpaceNode as LeafPsiElement).rawReplaceWithText("\n${whiteSpaceNode.text}")
if (whiteSpaceNode.text.lines().size == 1) {
(whiteSpaceNode as LeafPsiElement).rawReplaceWithText("\n${whiteSpaceNode.text}")
}
} else {
addChild(PsiWhiteSpaceImpl("\n"), beforeNode)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength)
}

@Test
fun `shouldn't fix`() {
fixAndCompare("LongExpressionNoFixExpected.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength)
fun `shouldn't fix1`() {
fixAndCompare("LongExpressionNoFixExpected1.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength)
}

@Test
fun `shouldn't fix2`() {
fixAndCompare("LongExpressionNoFixExpected2.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength)
}

@Test
Expand All @@ -88,4 +93,11 @@ class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength)
fun `fix expression in condition`() {
fixAndCompare("LongExpressionInConditionExpected.kt", "LongExpressionInConditionTest.kt", rulesConfigListLineLength)
}

@Test
fun `fix long Dot Qualified Expression`() {
fixAndCompare("LongDotQualifiedExpressionExpected.kt", "LongDotQualifiedExpressionTest.kt", rulesConfigListLineLength)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ class LineLengthWarnTest : LintTestBase(::LineLength) {
| }
|}
""".trimMargin(),
LintError(14, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 143", false),
LintError(18, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 142", false)
LintError(14, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 143", true),
LintError(18, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 142", true)
)
}

Expand Down Expand Up @@ -223,7 +223,8 @@ class LineLengthWarnTest : LintTestBase(::LineLength) {
|}
""".trimMargin(),
LintError(8, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 130", false),
LintError(9, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 123", false)
LintError(9, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 123", true)

)
}

Expand All @@ -235,7 +236,7 @@ class LineLengthWarnTest : LintTestBase(::LineLength) {
|@Query(value = "ASDAASDASDASDASDASDASDASDAASDASDASDASDASDASDASDAASDASDASDASDASDASD")
|fun foo() = println("ASDAASDASDASDASDASDASDASDAASDASDASDASDASDASDASDAASDASDASDASDASDASD")
""".trimMargin(),
LintError(1, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 84", false),
LintError(1, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 84", true),
LintError(2, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 89", true),
rulesConfigList = shortLineLength
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ fun foo() {
val variable =
Methoooooooooooooooooooooooooood() ?: "some loooooong string"

val variable = Methooooood() ?: "some" +
" looong string"
val variable = Methooooood()
?: "some looong string"

var headerKdoc = firstCodeNode.prevSibling {
it.elementType == KDOC
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package test.paragraph3.long_line

val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot
.Qualified.Expression
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package test.paragraph3.long_line

@Query(value = "select * from test inner join" +
" test_execution on test.id = test_execution.test_id and test_execution.st", nativeQuery = true)
@Query(
value = "select * from test inner join test_execution on test.id = test_execution.test_id and test_execution.st",
nativeQuery = true)
fun retrieveBatches(limit: Int, offset: Int, executionId: Long): Some

@Query(value = "select * from test inner join" +
" test_execution on test.id = test_execution.test_id and test_execution.status = 'READY' and test_execution.test_suite_execution_id = ?3 limit ?1 offset ?2", nativeQuery = true)
@Query(value = "select * from test inner join test_execution on test.id = test_execution.test_id and test_execution.status = 'READY' and test_execution.test_suite_execution_id = ?3 limit ?1 offset ?2", nativeQuery = true)
fun some(limit: Int, offset: Int, executionId: Long): List<Test>

@Query(value = "select * from test inner joi", nativeQuery = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package test.paragraph3.long_line

fun foo() =
println("fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd")
fun foo() = println(
"fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd"
)

fun foo () { println("fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd") }
fun foo () { println(
"fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd"
) }
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ fun foo() {
fun foo() {
val veryLoooooooooooooooooongNamesList =
listOf<String>("Jack", "Nick")
veryLoooooooooooooooooongNamesList.forEach {
veryLoooooooooooooooooongNamesList
.forEach {
name ->
if (name == "Nick") {
veryLoooooooooooooooooongNamesList.map { val str = "This string shouldn't be split"}
name.map { val str =
"This string should be split" }
}

}
}

Expand All @@ -33,10 +33,11 @@ fun foo() {
val longStringExpression = "First part" +
"second Part"

val longStringExpression = "First" + "second Part"
val longStringExpression = "First" +
"second Part"

val longStringExpression = "First very long" +
" part" + "second Part"
val longStringExpression =
"First very long part" + "second Part"

val longStringExpression2 =
"String starts at the line len limit"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
package test.paragraph3.long_line

val LongWithVar2 = "${s + "a"} is a string"
val LongWithVar2 =
"${s + "a"} is a string"

0 comments on commit 5e52117

Please sign in to comment.