Skip to content

Commit

Permalink
Don't change the indent for parentheses-enclosed expressions
Browse files Browse the repository at this point in the history
### What's done:

 * From now on, parentheses (`LPAR`/`RPAR`) don't increment or decrement the
   indent unless they're a part of a function declaration or a function call.
 * Parentheses which just enclose an expression don't affect the indent at all.
 * This fixes #1409.
  • Loading branch information
0x6675636b796f75676974687562 committed Jun 28, 2022
1 parent 5f77cad commit 023496c
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.cqfn.diktat.ruleset.utils.isSpaceCharacter
import org.cqfn.diktat.ruleset.utils.lastIndent
import org.cqfn.diktat.ruleset.utils.leaveOnlyOneNewLine

import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.CLOSING_QUOTE
import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
Expand All @@ -45,6 +46,7 @@ import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY
import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_END
import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_START
import com.pinterest.ktlint.core.ast.ElementType.LPAR
import com.pinterest.ktlint.core.ast.ElementType.PARENTHESIZED
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.RBRACKET
import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
Expand All @@ -55,6 +57,8 @@ import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY
import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
import com.pinterest.ktlint.core.ast.ElementType.THEN
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.visit
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
Expand All @@ -73,6 +77,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.slf4j.LoggerFactory

import kotlin.math.abs
import kotlin.reflect.KCallable

/**
* Rule that checks indentation. The following general rules are checked:
Expand Down Expand Up @@ -176,9 +181,9 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
val context = IndentContext(configuration)
node.visit { astNode ->
context.checkAndReset(astNode)
if (astNode.elementType in increasingTokens) {
if (astNode.isIndentIncrementing()) {
context.storeIncrementingToken(astNode.elementType)
} else if (astNode.elementType in decreasingTokens && !astNode.treePrev.let { it.elementType == WHITE_SPACE && it.textContains(NEWLINE) }) {
} else if (astNode.isIndentDecrementing() && !astNode.treePrev.let { it.elementType == WHITE_SPACE && it.textContains(NEWLINE) }) {
// if decreasing token is after WHITE_SPACE with \n, indents are corrected in visitWhiteSpace method
context.dec(astNode.elementType)
} else if (astNode.elementType == WHITE_SPACE && astNode.textContains(NEWLINE) && astNode.treeNext != null) {
Expand Down Expand Up @@ -208,7 +213,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
context.maybeIncrement()
positionByOffset = astNode.treeParent.calculateLineColByOffset()
val whiteSpace = astNode.psi as PsiWhiteSpace
if (astNode.treeNext.elementType in decreasingTokens) {
if (astNode.treeNext.isIndentDecrementing()) {
// if newline is followed by closing token, it should already be indented less
context.dec(astNode.treeNext.elementType)
}
Expand Down Expand Up @@ -493,7 +498,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
}
regularIndent -= config.indentationSize
}
if (activeTokens.isNotEmpty() && activeTokens.peek() == matchingTokens.find { it.second == token }?.first) {
if (activeTokens.isNotEmpty() && activeTokens.peek() == token.braceMatchOrNull()) {
activeTokens.pop()
}
}
Expand Down Expand Up @@ -546,11 +551,20 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
companion object {
private val log = LoggerFactory.getLogger(IndentationRule::class.java)
const val NAME_ID = "indentation"
private val increasingTokens = listOf(LPAR, LBRACE, LBRACKET, LONG_TEMPLATE_ENTRY_START)
private val decreasingTokens = listOf(RPAR, RBRACE, RBRACKET, LONG_TEMPLATE_ENTRY_END)
private val matchingTokens = increasingTokens.zip(decreasingTokens)
private val increasingTokens: Set<IElementType> = linkedSetOf(LPAR, LBRACE, LBRACKET, LONG_TEMPLATE_ENTRY_START)
private val decreasingTokens: Set<IElementType> = linkedSetOf(RPAR, RBRACE, RBRACKET, LONG_TEMPLATE_ENTRY_END)

/**
* This is essentially a bi-map, which allows to look up a closing brace
* type by an opening brace type, or vice versa.
*/
private val matchingTokens = (increasingTokens.asSequence() zip decreasingTokens.asSequence()).flatMap { (opening, closing) ->
sequenceOf(opening to closing, closing to opening)
}.toMap()
private val stringLiteralTokens = listOf(SHORT_STRING_TEMPLATE_ENTRY, LONG_STRING_TEMPLATE_ENTRY)
private val knownTrimFunctionPatterns = setOf("trimIndent", "trimMargin")
private val knownTrimFunctionPatterns = sequenceOf(String::trimIndent, String::trimMargin)
.map(KCallable<String>::name)
.toSet()

/**
* @return a string which consists of `N` [space][SPACE] characters.
Expand All @@ -560,6 +574,60 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
get() =
SPACE.toString().repeat(n = this)

/**
* @return `true` if the indent should be incremented once this node is
* encountered, `false` otherwise.
* @see isIndentDecrementing
*/
private fun ASTNode.isIndentIncrementing(): Boolean =
when (elementType) {
/*
* This is a special case of an opening parenthesis which *may* or
* *may not* be indent-incrementing.
*/
LPAR -> isParenthesisAffectingIndent()

else -> elementType in increasingTokens
}

/**
* @return `true` if the indent should be decremented once this node is
* encountered, `false` otherwise.
* @see isIndentIncrementing
*/
private fun ASTNode.isIndentDecrementing(): Boolean =
when (elementType) {
/*
* This is a special case of a closing parenthesis which *may* or
* *may not* be indent-decrementing.
*/
RPAR -> isParenthesisAffectingIndent()

else -> elementType in decreasingTokens
}

/**
* Parentheses only affect indent when they're a part of a
* [VALUE_PARAMETER_LIST] (formal arguments) or a [VALUE_ARGUMENT_LIST]
* (effective function call arguments).
*
* When they're children of a [PARENTHESIZED] (often inside a
* [BINARY_EXPRESSION]), they don't contribute to the indent.
*
* @return whether this [LPAR] or [RPAR] node affects indent.
* @see BINARY_EXPRESSION
* @see PARENTHESIZED
* @see VALUE_ARGUMENT_LIST
* @see VALUE_PARAMETER_LIST
*/
private fun ASTNode.isParenthesisAffectingIndent(): Boolean {
require(elementType in arrayOf(LPAR, RPAR)) {
elementType.toString()
}

return treeParent.elementType != PARENTHESIZED
}

/**
* @return `true` if this is a [String.trimIndent] or [String.trimMargin]
* call, `false` otherwise.
Expand All @@ -584,6 +652,13 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
return identifier.text in knownTrimFunctionPatterns
}

/**
* @return the matching closing brace type for this opening brace type,
* or vice versa.
*/
private fun IElementType.braceMatchOrNull(): IElementType? =
matchingTokens[this]

/**
* Checks this [REGULAR_STRING_PART] child of a [LITERAL_STRING_TEMPLATE_ENTRY].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ internal object IndentationRuleTestResources {
|fun foo() =
| println()
""".trimMargin(),

"""
|fun f() = x + (y +
| g(x)
|)
""".trimMargin(),

"""
|fun f() = (1 +
| 2)
""".trimMargin(),
)

/**
Expand Down Expand Up @@ -173,6 +184,17 @@ internal object IndentationRuleTestResources {
|fun foo() =
| println()
""".trimMargin(),

"""
|fun f() = x + (y +
| g(x)
|)
""".trimMargin(),

"""
|fun f() = (1 +
| 2)
""".trimMargin(),
)

/**
Expand Down Expand Up @@ -422,6 +444,12 @@ internal object IndentationRuleTestResources {
| 3
""".trimMargin(),

"""
|val value1a = (1 to
| 2 to
| 3)
""".trimMargin(),

"""
|val value2 =
| 1 to
Expand Down Expand Up @@ -653,6 +681,12 @@ internal object IndentationRuleTestResources {
| 3
""".trimMargin(),

"""
|val value1a = (1 to
| 2 to
| 3)
""".trimMargin(),

"""
|val value2 =
| 1 to
Expand Down

0 comments on commit 023496c

Please sign in to comment.