com.pinterest.ktlint.ruleset.standard.rules.IndentationRule.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ktlint-ruleset-standard Show documentation
Show all versions of ktlint-ruleset-standard Show documentation
An anti-bikeshedding Kotlin linter with built-in formatter.
package com.pinterest.ktlint.ruleset.standard.rules
import com.pinterest.ktlint.logger.api.initKtLintKLogger
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARRAY_ACCESS_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW
import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.BINARY_WITH_TYPE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK
import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.BODY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CALL_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CATCH
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLOSING_QUOTE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONDITION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_DELEGATION_CALL
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.DELEGATED_SUPER_TYPE_ENTRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.DESTRUCTURING_DECLARATION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.DOT_QUALIFIED_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELSE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ELVIS
import com.pinterest.ktlint.rule.engine.core.api.ElementType.EQ
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FINALLY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUNCTION_LITERAL
import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER
import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF
import com.pinterest.ktlint.rule.engine.core.api.ElementType.IS_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC
import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_END
import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_LEADING_ASTERISK
import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC_START
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LBRACKET
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LITERAL_STRING_TEMPLATE_ENTRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LONG_STRING_TEMPLATE_ENTRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.LPAR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.NULLABLE_TYPE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_DECLARATION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPEN_QUOTE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PARENTHESIZED
import com.pinterest.ktlint.rule.engine.core.api.ElementType.POSTFIX_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PREFIX_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PRIMARY_CONSTRUCTOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY_ACCESSOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RBRACKET
import com.pinterest.ktlint.rule.engine.core.api.ElementType.REGULAR_STRING_PART
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RETURN_KEYWORD
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.SAFE_ACCESS_EXPRESSION
import com.pinterest.ktlint.rule.engine.core.api.ElementType.SECONDARY_CONSTRUCTOR
import com.pinterest.ktlint.rule.engine.core.api.ElementType.STRING_TEMPLATE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.SUPER_TYPE_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.THEN
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPEALIAS
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_CONSTRAINT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_CONSTRAINT_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PARAMETER_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.USER_TYPE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN_ENTRY
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHERE_KEYWORD
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHILE
import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHITE_SPACE
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.IndentStyle.SPACE
import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.IndentStyle.TAB
import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED
import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler
import com.pinterest.ktlint.rule.engine.core.api.RuleId
import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint
import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE
import com.pinterest.ktlint.rule.engine.core.api.children
import com.pinterest.ktlint.rule.engine.core.api.column
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue.ktlint_official
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty
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.firstChildLeafOrSelf
import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed
import com.pinterest.ktlint.rule.engine.core.api.indent
import com.pinterest.ktlint.rule.engine.core.api.isPartOf
import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment
import com.pinterest.ktlint.rule.engine.core.api.isRoot
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithoutNewline
import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf
import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.nextSibling
import com.pinterest.ktlint.rule.engine.core.api.parent
import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
import com.pinterest.ktlint.rule.engine.core.api.prevSibling
import com.pinterest.ktlint.rule.engine.core.api.remove
import com.pinterest.ktlint.ruleset.standard.StandardRule
import io.github.oshai.kotlinlogging.KotlinLogging
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.PsiComment
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.leaves
import org.jetbrains.kotlin.psi.psiUtil.parents
import java.util.Deque
import java.util.LinkedList
private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger()
@SinceKtlint("0.1", STABLE)
public class IndentationRule :
StandardRule(
id = "indent",
visitorModifiers =
setOf(
VisitorModifier.RunAsLateAsPossible,
VisitorModifier.RunAfterRule(
ruleId = CLASS_SIGNATURE_RULE_ID,
mode = REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED,
),
VisitorModifier.RunAfterRule(
ruleId = FUNCTION_SIGNATURE_RULE_ID,
mode = REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED,
),
VisitorModifier.RunAfterRule(
ruleId = TRAILING_COMMA_ON_CALL_SITE_RULE_ID,
mode = REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED,
),
VisitorModifier.RunAfterRule(
ruleId = TRAILING_COMMA_ON_DECLARATION_SITE_RULE_ID,
mode = REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED,
),
),
usesEditorConfigProperties =
setOf(
CODE_STYLE_PROPERTY,
INDENT_SIZE_PROPERTY,
INDENT_STYLE_PROPERTY,
INDENT_WHEN_ARROW_ON_NEW_LINE,
),
),
RuleAutocorrectApproveHandler {
private var codeStyle = CODE_STYLE_PROPERTY.defaultValue
private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG
private var indentWhenArrowOnNewLine = INDENT_WHEN_ARROW_ON_NEW_LINE.defaultValue
private var line = 1
private val indentContextStack: Deque = LinkedList()
private lateinit var stringTemplateIndenter: StringTemplateIndenter
override fun beforeFirstNode(editorConfig: EditorConfig) {
codeStyle = editorConfig[CODE_STYLE_PROPERTY]
indentConfig =
IndentConfig(
indentStyle = editorConfig[INDENT_STYLE_PROPERTY],
tabWidth = editorConfig[INDENT_SIZE_PROPERTY],
)
if (indentConfig.disabled) {
stopTraversalOfAST()
}
indentWhenArrowOnNewLine = editorConfig[INDENT_WHEN_ARROW_ON_NEW_LINE]
}
override fun beforeVisitChildNodes(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision,
) {
if (node.isRoot()) {
// File should not start with a whitespace
node
.nextLeaf()
?.takeIf { it.isWhiteSpaceWithoutNewline() }
?.let { whitespaceWithoutNewline ->
emit(node.startOffset, "Unexpected indentation", true)
.ifAutocorrectAllowed { whitespaceWithoutNewline.remove() }
}
indentContextStack.addLast(startNoIndentZone(node))
}
when {
node.isWhiteSpaceWithNewline() -> {
line++
if (indentContextStack.peekLast()?.activated == false) {
val lastIndentContext = indentContextStack.removeLast()
indentContextStack.addLast(
lastIndentContext.copy(activated = true),
)
}
visitNewLineIndentation(node, emit)
}
node.elementType == CONTEXT_RECEIVER_LIST ||
node.elementType == LONG_STRING_TEMPLATE_ENTRY ||
node.elementType == STRING_TEMPLATE ||
node.elementType == VALUE_ARGUMENT_LIST -> {
startIndentContext(
fromAstNode = node,
lastChildIndent = "",
)
}
(node.elementType == SUPER_TYPE_LIST && !node.isPrecededByComment()) ||
(node.isPartOfComment() && node.nextCodeSibling()?.elementType == SUPER_TYPE_LIST) -> {
if (codeStyle == ktlint_official) {
val superTypeList =
if (node.isPartOfComment()) {
node.nextCodeLeaf()!!
} else {
node
}
if (superTypeList.isPartOfClassWithAMultilinePrimaryConstructor()) {
// Contrary to the default IntelliJ IDEA formatter, indent the super type call entry so that it looks better in case it
// is followed by another super type:
// class Foo(
// val bar1: Bar,
// val bar2: Bar,
// ) : FooBar(
// bar1,
// bar2
// ),
// BarFoo,
startIndentContext(
fromAstNode = node,
activated = true,
)
}
}
}
node.elementType == VALUE_ARGUMENT -> {
visitValueArgument(node)
}
node.elementType == SECONDARY_CONSTRUCTOR -> {
visitSecondaryConstructor(node)
}
node.elementType == PARENTHESIZED -> {
if (codeStyle == ktlint_official) {
// Contrary to the IntelliJ IDEA default formatter, do not indent the closing RPAR
startIndentContext(
fromAstNode = node,
lastChildIndent = "",
)
} else if (node.treeParent.treeParent.elementType != IF) {
startIndentContext(node)
}
}
node.elementType == TYPE_ARGUMENT_LIST ||
node.elementType == TYPE_PARAMETER_LIST -> {
if (codeStyle == ktlint_official) {
// Contrary to the IntelliJ IDEA default formatter, do not indent the closing angle bracket
startIndentContext(
fromAstNode = node,
lastChildIndent = "",
)
} else {
startIndentContext(node)
}
}
node.elementType == BINARY_WITH_TYPE ||
node.elementType == USER_TYPE -> {
startIndentContext(node)
}
node.elementType == IS_EXPRESSION ||
node.elementType == PREFIX_EXPRESSION ||
node.elementType == POSTFIX_EXPRESSION -> {
startIndentContext(node)
}
node.elementType == DELEGATED_SUPER_TYPE_ENTRY ||
node.elementType == ANNOTATED_EXPRESSION ||
node.elementType == TYPE_REFERENCE -> {
startIndentContext(
fromAstNode = node,
childIndent = "",
)
}
node.elementType == IF -> {
visitIf(node)
}
node.elementType == LBRACE -> {
visitLbrace(node)
}
node.elementType == VALUE_PARAMETER_LIST &&
node.treeParent.elementType != FUNCTION_LITERAL -> {
startIndentContext(
fromAstNode = node,
lastChildIndent = "",
)
}
node.elementType == LPAR &&
node.nextCodeSibling()?.elementType == CONDITION -> {
visitLparBeforeCondition(node)
}
node.elementType == VALUE_PARAMETER -> {
visitValueParameter(node)
}
node.elementType == FUN -> {
visitFun(node)
}
node.elementType == CLASS -> {
visitClass(node)
}
node.elementType == OBJECT_DECLARATION -> {
visitObjectDeclaration(node)
}
node.elementType == BINARY_EXPRESSION -> {
visitBinaryExpression(node)
}
node.elementType in CHAINABLE_EXPRESSION -> {
if (codeStyle == ktlint_official &&
node.elementType == DOT_QUALIFIED_EXPRESSION &&
node.treeParent?.elementType == ARRAY_ACCESS_EXPRESSION &&
node.treeParent?.treeParent?.elementType == CALL_EXPRESSION
) {
// Issue 1540: Deviate and fix from incorrect formatting in IntelliJ IDEA formatting and produce following:
// val fooBar2 = foo
// .bar[0] {
// "foobar"
// }
startIndentContext(
fromAstNode = node.treeParent,
toAstNode = node.treeParent.treeParent.lastChildLeafOrSelf(),
)
} else if (node.prevCodeSibling().isElvisOperator()) {
startIndentContext(node)
} else if (node.treeParent.elementType in CHAINABLE_EXPRESSION) {
// Multiple dot qualified expressions and/or safe expression on the same line should not increase the indent level
} else {
startIndentContext(node)
}
}
node.elementType == IDENTIFIER &&
node.treeParent.elementType == PROPERTY -> {
visitIdentifierInProperty(node)
}
node.elementType == LITERAL_STRING_TEMPLATE_ENTRY &&
node.nextCodeSibling()?.elementType == CLOSING_QUOTE -> {
visitWhiteSpaceBeforeClosingQuote(node, emit)
}
node.elementType == WHEN -> {
visitWhen(node)
}
node.elementType == WHEN_ENTRY -> {
visitWhenEntry(node)
}
node.elementType == WHERE_KEYWORD &&
node.nextCodeSibling()?.elementType == TYPE_CONSTRAINT_LIST -> {
visitWhereKeywordBeforeTypeConstraintList(node)
}
node.elementType == KDOC -> {
visitKdoc(node)
}
node.elementType == PROPERTY_ACCESSOR ||
node.elementType == TYPEALIAS -> {
visitPropertyAccessor(node)
}
node.elementType == FOR ||
node.elementType == WHILE -> {
visitConditionalLoop(node)
}
node.elementType == LBRACKET -> {
visitLBracket(node)
}
node.elementType == NULLABLE_TYPE -> {
visitNullableType(node)
}
node.elementType == DESTRUCTURING_DECLARATION -> {
visitDestructuringDeclaration(node)
}
node.elementType == TRY -> {
visitTryCatchFinally(node)
}
else -> {
LOGGER.trace { "No processing for ${node.elementType}: ${node.textWithEscapedTabAndNewline()}" }
}
}
}
private fun ASTNode.isPartOfClassWithAMultilinePrimaryConstructor() =
treeParent
.takeIf { it.elementType == CLASS }
?.findChildByType(PRIMARY_CONSTRUCTOR)
?.textContains('\n') == true
private fun visitValueArgument(node: ASTNode) {
if (codeStyle == ktlint_official) {
// Deviate from standard IntelliJ IDEA formatting to allow formatting below:
// val foo = foo(
// parameterName =
// "The quick brown fox "
// .plus("jumps ")
// .plus("over the lazy dog"),
// )
startIndentContext(
fromAstNode = node,
lastChildIndent = "",
)
}
}
private fun visitSecondaryConstructor(node: ASTNode) {
node
.findChildByType(CONSTRUCTOR_DELEGATION_CALL)
?.let { constructorDelegationCall ->
val fromAstNode = node.skipLeadingWhitespaceCommentsAndAnnotations()
val nextToAstNode =
startIndentContext(
fromAstNode = constructorDelegationCall,
).prevCodeLeaf()
// Leading annotations and comments should be indented at same level as constructor itself
if (fromAstNode != node.nextLeaf()) {
startIndentContext(
fromAstNode = node,
toAstNode = nextToAstNode,
childIndent = "",
)
}
}
}
private fun visitIf(node: ASTNode) {
var nextToAstNode = node.lastChildLeafOrSelf()
node
.findChildByType(ELSE)
?.let { fromAstNode ->
nextToAstNode =
startIndentContext(
fromAstNode = fromAstNode,
toAstNode = nextToAstNode,
).prevCodeLeaf()
}
node
.findChildByType(THEN)
?.lastChildLeafOrSelf()
?.nextLeaf()
?.let { nodeAfterThenBlock ->
nextToAstNode =
startIndentContext(
fromAstNode = nodeAfterThenBlock,
toAstNode = nextToAstNode,
childIndent = "",
).prevCodeLeaf()
}
node
.findChildByType(RPAR)
?.nextCodeLeaf()
?.let { nodeAfterConditionBlock ->
nextToAstNode =
startIndentContext(
fromAstNode = nodeAfterConditionBlock,
toAstNode = nextToAstNode,
).prevCodeLeaf()
}
// No indent for the RPAR
startIndentContext(
fromAstNode = node,
toAstNode = nextToAstNode,
lastChildIndent = "",
)
}
private fun visitLbrace(node: ASTNode) {
// Outer indent context
val rbrace =
requireNotNull(
node.nextSibling { it.elementType == RBRACE },
) { "Can not find matching rbrace" }
startIndentContext(
fromAstNode = node,
toAstNode = rbrace,
firstChildIndent = "",
lastChildIndent = "",
)
// Inner indent context in reversed order
node
.treeParent
?.takeIf { it.elementType == FUNCTION_LITERAL }
?.findChildByType(ARROW)
?.let { arrow ->
startIndentContext(
fromAstNode = arrow,
toAstNode = rbrace,
lastChildIndent = "",
)
startIndentContext(
fromAstNode = node,
toAstNode = arrow.prevCodeLeaf()!!,
childIndent = arrow.calculateIndentOfFunctionLiteralParameters(),
)
}
}
private fun ASTNode.calculateIndentOfFunctionLiteralParameters() =
if (isFirstParameterOfFunctionLiteralPrecededByNewLine()) {
if (codeStyle == ktlint_official) {
// Indent with single indent as defined in Kotlin Coding conventions
indentConfig.indent
} else {
// Comply with default IDEA formatting although it is not compliant with Kotlin Coding conventions
// val fieldExample =
// LongNameClass {
// paramA,
// paramB,
// paramC ->
// ClassB(paramA, paramB, paramC)
// }
indentConfig.indent.repeat(2)
}
} else {
// Allow default IntelliJ IDEA formatting:
// val fieldExample =
// LongNameClass { paramA,
// paramB,
// paramC ->
// ClassB(paramA, paramB, paramC)
// }
// val fieldExample =
// someFunction(
// 1,
// 2,
// ) { paramA,
// paramB,
// paramC ->
// ClassB(paramA, paramB, paramC)
// }
this
.takeIf { it.isPartOf(CALL_EXPRESSION) }
?.treeParent
?.leaves(false)
?.takeWhile { !it.isWhiteSpaceWithNewline() }
?.sumOf { it.textLength }
?.plus(2) // need to add spaces to compensate for "{ "
?.let { length -> " ".repeat(length) }
?: indentConfig.indent.repeat(2)
}
private fun ASTNode.isFirstParameterOfFunctionLiteralPrecededByNewLine() =
parent(FUNCTION_LITERAL)
?.findChildByType(VALUE_PARAMETER_LIST)
?.prevSibling { it.textContains('\n') } != null
private fun visitLparBeforeCondition(node: ASTNode) {
startIndentContext(
// Allow to pickup whitespace before condition
fromAstNode = requireNotNull(node.nextLeaf()),
// Ignore whitespace after condition but before rpar
toAstNode = requireNotNull(node.nextCodeSibling()).lastChildLeafOrSelf(),
nodeIndent = currentIndent() + indentConfig.indent,
childIndent = "",
)
}
private fun visitValueParameter(node: ASTNode) {
// Inner indent contexts in reversed order
var nextToAstNode: ASTNode = node.lastChildLeafOrSelf()
node
.findChildByType(EQ)
?.let { fromAstNode ->
nextToAstNode =
startIndentContext(
fromAstNode = fromAstNode,
toAstNode = nextToAstNode,
).prevCodeLeaf()
}
if (codeStyle == ktlint_official) {
// Deviate from standard IntelliJ IDEA formatting to allow formatting below:
// fun process(
// aVariableWithAVeryLongName:
// TypeWithAVeryLongNameThatDoesNotFitOnSameLineAsTheVariableName
// ): List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy