Skip to content

Commit

Permalink
Add '.editorconfig' property 'ij_kotlin_indent_before_arrow_on_new_li…
Browse files Browse the repository at this point in the history
…ne' (#2838)

When 'ij_kotlin_indent_before_arrow_on_new_line' is enabled, the arrow in a when-entry should be indented when it starts on a new line.
* For 'intellij_idea' and 'android_studio' keep formatting same as the ugly but default IDEA formatting. Note that the closing curly brace is de-indented compared to arrow. For those code styles this property needs to be set to true to keep formatting compatible with default IDEA Formatting when using IDEA version 2024.2 or above.
* For 'ktlint_official' code style apply a more consistent formatting by indenting the entire block body (including closing curly brace) relative to the indented arrow.

Closes #2808
  • Loading branch information
paul-dingemans authored Oct 19, 2024
1 parent a73d6b3 commit 11f01c1
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 26 deletions.
9 changes: 5 additions & 4 deletions documentation/snapshot/docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -1190,10 +1190,11 @@ Indentation formatting - respects `.editorconfig` `indent_size` with no continua
!!! note
This rule handles indentation for many different language constructs which can not be summarized with a few examples. See the [unit tests](https://github.com/pinterest/ktlint/blob/master/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleTest.kt) for more details.

| Configuration setting | ktlint_official | intellij_idea | android_studio |
|:------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:|
| `indent_size`</br><i>The size of an indentation level when `indent_style` is set to `space`. Use value `unset` to ignore indentation.</i> | 4 | 4 | 4 |
| `indent_style`</br><i>Style of indentation. Set this value to `space` or `tab`.</i> | `space` | `space` | `space` |
| Configuration setting | ktlint_official | intellij_idea | android_studio |
|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------:|:-------------:|:--------------:|
| `indent_size`</br><i>The size of an indentation level when `indent_style` is set to `space`. Use value `unset` to ignore indentation.</i> | 4 | 4 | 4 |
| `indent_style`</br><i>Style of indentation. Set this value to `space` or `tab`.</i> | `space` | `space` | `space` |
| `ij_kotlin_indent_before_arrow_on_new_line`</br><i>Indent the arrow in a when-entry if the arrow starts on a new line. Set this value to `true` or `false`. Starting from IDEA version `2024.2` or above this value needs to be set to `true` to maintain compatibility with IDEA formatting.</i> | `false` | `false` | `false` |

Rule id: `standard:indent`

Expand Down
7 changes: 5 additions & 2 deletions ktlint-ruleset-standard/api/ktlint-ruleset-standard.api
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,18 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRul
}

public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRule : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler {
public static final field KDOC_CONTINUATION_INDENT Ljava/lang/String;
public static final field TYPE_CONSTRAINT_CONTINUATION_INDENT Ljava/lang/String;
public static final field Companion Lcom/pinterest/ktlint/ruleset/standard/rules/IndentationRule$Companion;
public fun <init> ()V
public fun afterLastNode ()V
public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V
public fun beforeFirstNode (Lcom/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig;)V
public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V
}

public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRule$Companion {
public final fun getINDENT_WHEN_ARROW_ON_NEW_LINE ()Lcom/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfigProperty;
}

public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleKt {
public static final fun getINDENTATION_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERT
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
Expand All @@ -120,6 +121,8 @@ 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
Expand Down Expand Up @@ -160,11 +163,13 @@ public class IndentationRule :
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

Expand All @@ -182,6 +187,7 @@ public class IndentationRule :
if (indentConfig.disabled) {
stopTraversalOfAST()
}
indentWhenArrowOnNewLine = editorConfig[INDENT_WHEN_ARROW_ON_NEW_LINE]
}

override fun beforeVisitChildNodes(
Expand Down Expand Up @@ -827,23 +833,57 @@ public class IndentationRule :
node
.findChildByType(ARROW)
?.let { arrow ->
val outerIndent =
if (arrow.nextLeaf()?.elementType == BLOCK) {
""
} else {
indentConfig.indent
arrow
.prevSibling { !it.isPartOfComment() }
.let { prevSibling ->
if (indentWhenArrowOnNewLine && prevSibling != null && prevSibling.isWhiteSpaceWithNewline()) {
if (arrow.nextCodeSibling()?.elementType == BLOCK && codeStyle != ktlint_official) {
// Uglify the indentation to below to keep compatible with default formatting Intellij IDEA
// val foo =
// when (bar()) {
// is Bar1
// -> {
// "bar1"
// }
// }
startIndentContext(
fromAstNode = prevSibling,
toAstNode = node.lastChildLeafOrSelf(),
firstChildIndent = indentConfig.indent,
childIndent = "",
)
} else {
// Reformat to below. This is not compatible with default IDEA formatting. But the closing brace is now at
// least aligned consistently.
// val foo =
// when (bar()) {
// is Bar1
// -> {
// "bar1"
// }
// }
startIndentContext(
fromAstNode = arrow.nextLeaf()!!,
toAstNode = node.lastChildLeafOrSelf(),
)
startIndentContext(
fromAstNode = node,
toAstNode = prevSibling.prevLeaf()!!,
childIndent = "",
)
}
} else {
startIndentContext(
fromAstNode = arrow.nextLeaf()!!,
toAstNode = node.lastChildLeafOrSelf(),
)
startIndentContext(
fromAstNode = node,
toAstNode = arrow,
childIndent = "",
)
}
}
startIndentContext(
fromAstNode = arrow.nextLeaf()!!,
toAstNode = node.lastChildLeafOrSelf(),
firstChildIndent = outerIndent,
lastChildIndent = outerIndent,
)
startIndentContext(
fromAstNode = node,
toAstNode = arrow,
childIndent = "",
)
}
}

Expand Down Expand Up @@ -1290,10 +1330,25 @@ public class IndentationRule :

private fun ASTNode.isPrecededByComment() = prevSibling { !it.isWhiteSpace() }?.isPartOfComment() == true

private companion object {
const val KDOC_CONTINUATION_INDENT = " "
const val TYPE_CONSTRAINT_CONTINUATION_INDENT = " " // Length of keyword "where" plus separating space
val CHAINABLE_EXPRESSION = setOf(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
public companion object {
private const val KDOC_CONTINUATION_INDENT = " "
private const val TYPE_CONSTRAINT_CONTINUATION_INDENT = " " // Length of keyword "where" plus separating space
private val CHAINABLE_EXPRESSION = setOf(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)

public val INDENT_WHEN_ARROW_ON_NEW_LINE: EditorConfigProperty<Boolean> =
EditorConfigProperty(
type =
PropertyType.LowerCasingPropertyType(
"ij_kotlin_indent_before_arrow_on_new_line",
"Indent the arrow in a when-entry if the arrow starts on a new line.",
PropertyValueParser.BOOLEAN_VALUE_PARSER,
setOf("true", "false"),
),
// Up until (and including) Intellij IDEA version `2024.1.6` the arrow on a newline was not indented. In version `2024.2`
// the default behavior was changed to true. Disable by default to keep backward compatibility with older ktlint and IDEA
// versions.
defaultValue = false,
)
}

private data class IndentContext(
Expand Down
Loading

0 comments on commit 11f01c1

Please sign in to comment.